Unittest.TestCase.assertRaises: примеры (PYTHON)

Метод assertRaises для тестирования исключений в Python
Раздел: Тестирование, Утверждения
unittest.TestCase.assertRaises(exception, callable, args, kwargs, msg): None

Основы функции assertRaises

Метод assertRaises() является частью фреймворка unittest и предназначен для проверки возникновения исключений в тестируемом коде. Он используется в модульных тестах для подтверждения, что определенный фрагмент кода генерирует ожидаемый тип исключения при выполнении.

Сигнатура метода: assertRaises(expected_exception, callable, *args, **kwargs)

Параметры:

  • expected_exception - класс исключения или кортеж классов исключений, который ожидается при вызове.
  • callable - вызываемый объект (функция, метод, класс), который проверяется на выброс исключения.
  • *args - позиционные аргументы, передаваемые в вызываемый объект.
  • **kwargs - именованные аргументы, передаваемые в вызываемый объект.
  • msg - необязательное сообщение, выводимое при провале теста (доступен как keyword-only аргумент в контекстном менеджере).

Метод возвращает контекстный менеджер при использовании с оператором with, что позволяет проверить исключение от блока кода. Если исключение ожидаемого типа возникает, тест проходит успешно. При отсутствии исключения или возникновении исключения другого типа тест завершается ошибкой.

Базовые примеры использования

Проверка возникновения исключения при делении на ноль:

import unittest

def divide(a, b):
    return a / b

class TestExample(unittest.TestCase):
    def test_divide_by_zero(self):
        self.assertRaises(ZeroDivisionError, divide, 10, 0)

if __name__ == '__main__':
    unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

Использование в качестве контекстного менеджера:

class TestExample(unittest.TestCase):
    def test_with_context_manager(self):
        with self.assertRaises(ValueError):
            int('не число')

    def test_with_context_and_message(self):
        with self.assertRaises(TypeError, msg='Ожидалась ошибка типа'):
            'строка' + 123
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

Похожие функции в Python

assertRaisesRegex - проверяет соответствие текста исключения регулярному выражению. Полезен, когда важно содержание сообщения об ошибке.

class TestAlternatives(unittest.TestCase):
    def test_regex(self):
        with self.assertRaisesRegex(ValueError, 'invalid literal'):
            int('abc')

pytest.raises - аналог в фреймворке pytest с более гибким синтаксисом. Может использоваться как контекстный менеджер или декоратор.

import pytest

def test_pytest_raises():
    with pytest.raises(IndexError):
        [][0]

unittest.TestCase.assertWarns - проверяет возникновение предупреждений вместо исключений.

Выбор между методами зависит от фреймворка тестирования. В рамках unittest предпочтительнее использовать assertRaises. Для проверки точного текста исключения подходит assertRaisesRegex.

Аналоги в других языках программирования

JavaScript (Jest): используется метод toThrow.

test('деление на ноль', () => {
  expect(() => 1/0).not.toThrow();
  expect(() => { throw new Error('ошибка') }).toThrow();
});

Java (JUnit 5): метод assertThrows возвращает исключение для дальнейшей проверки.

@Test
void testException() {
    Exception exception = assertThrows(
        ArithmeticException.class,
        () -> { int a = 1 / 0; }
    );
    assertEquals("/ by zero", exception.getMessage());
}

C# (NUnit): используется конструкция Assert.Throws.

[Test]
public void TestException()
{
    Assert.Throws(() => 
    { 
        int x = 10 / 0; 
    });
}

PHP (PHPUnit): метод expectException.

public function testException(): void
{
    $this->expectException(InvalidArgumentException::class);
    // код, вызывающий исключение
}

Основное отличие Python-реализации - поддержка как прямого вызова, так и контекстного менеджера. В других языках обычно используется один подход.

Типичные ошибки

Неправильная передача вызываемого объекта с аргументами:

class TestErrors(unittest.TestCase):
    def test_wrong_call(self):
        # Ошибка: исключение не возникает, так как функция вызывается сразу
        self.assertRaises(ValueError, int('abc'))  # Неправильно

    def test_correct_call(self):
        # Правильно: функция передается без вызова
        self.assertRaises(ValueError, int, 'abc')  # Правильно
E
======================================================================
ERROR: test_wrong_call (__main__.TestErrors)
----------------------------------------------------------------------
ValueError: invalid literal for int() with base 10: 'abc'

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)

Проверка не того типа исключения:

class TestErrors(unittest.TestCase):
    def test_wrong_exception(self):
        with self.assertRaises(TypeError):  # Ожидается TypeError
            int('abc')  # Фактически возникает ValueError
F
======================================================================
FAIL: test_wrong_exception (__main__.TestErrors)
----------------------------------------------------------------------
AssertionError: TypeError not raised

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)

История изменений

В Python 3.2 метод assertRaises стал поддерживать использование в качестве контекстного менеджера, что упростило проверку исключений в блоках кода.

В Python 3.4 добавлен метод assertRaisesRegex как замена устаревшему assertRaisesRegexp.

Начиная с Python 3.8, в сообщении об ошибке улучшено отображение информации при использовании контекстного менеджера.

В Python 3.11 были оптимизированы внутренние механизмы unittest, что повысило производительность выполнения тестов с assertRaises.

Расширенные примеры

Проверка нескольких типов исключений с помощью кортежа:

Пример python
class TestAdvanced(unittest.TestCase):
    def test_multiple_exceptions(self):
        with self.assertRaises((ValueError, TypeError)):
            # Возбуждает ValueError
            int(None)

    def test_exception_attributes(self):
        with self.assertRaises(OSError) as cm:
            open('несуществующий_файл.txt')
        self.assertEqual(cm.exception.errno, 2)
        self.assertEqual(cm.exception.filename, 'несуществующий_файл.txt')

Использование с пользовательскими исключениями:

Пример python
class CustomError(Exception):
    pass

def risky_function(x):
    if x < 0:
        raise CustomError('Отрицательное значение')
    return x * 2

class TestAdvanced(unittest.TestCase):
    def test_custom_exception(self):
        self.assertRaises(CustomError, risky_function, -5)
    
    def test_custom_with_context(self):
        with self.assertRaises(CustomError) as context:
            risky_function(-10)
        self.assertIn('Отрицательное', str(context.exception))

Интеграция с mock-объектами:

Пример python
from unittest.mock import Mock

class TestAdvanced(unittest.TestCase):
    def test_with_mock(self):
        mock_obj = Mock()
        mock_obj.method.side_effect = ValueError('Имитация ошибки')
        
        with self.assertRaises(ValueError):
            mock_obj.method()
        
        mock_obj.method.assert_called_once()

Проверка исключений в асинхронном коде (с использованием asynctest или unittest.IsolatedAsyncioTestCase):

Пример python
import asyncio

async def async_risky():
    raise RuntimeError('Асинхронная ошибка')

class TestAsync(unittest.IsolatedAsyncioTestCase):
    async def test_async_exception(self):
        with self.assertRaises(RuntimeError):
            await async_risky()
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

питон unittest.TestCase.assertRaises function comments

En
Unittest.TestCase.assertRaises Check exception is raised