Использование assertRaises() с NoneType объектами в Python
Быстрый ответ
Чтобы проверить возникновение исключений при работе с объектом None
, вызываемую функцию, генерирующую исключение, следует поместить внутрь блока assertRaises
. Образец кода приведён ниже:
import unittest
class TestNoneOperation(unittest.TestCase):
def test_none_method_call_raises(self):
with self.assertRaises(AttributeError):
none_var = None
none_var.invalid_method() # У None нет методов, подобно моим свободным воскресеньям ;)
unittest.main()
В данном примере мы убеждаемся, что при попытке вызвать несуществующий метод у объекта None
возникает исключение AttributeError
.
Корректный вызов assertRaises
Метод assertRaises()
принимает аргументы по отдельности: тип исключения, вызываемую функцию и аргументы для этой функции. Если функция будет вызвана до передачи в assertRaises
, тест перехватит исключение, не относящееся к тестируемым сценариям. Рассмотрим примеры:
# Это аналогично попытке бросить мяч в чужую корзину
self.assertRaises(ExceptionType, some_function(None)) # Неправильно! Попытка вызвать функцию приведёт к TypeError
# Мы точно знаем, куда бросаем мяч
self.assertRaises(ExceptionType, some_function, None) # Правильно! Вызываемая функция и её аргументы передаются раздельно
Если исключение TypeError
вызывается аргументами функции, используйте лямбда-выражение для отложенного вызова:
self.assertRaises(ExceptionType, lambda: some_function(None)) # Лямбда-функция отложит вызов аргумента
Проверка деталей исключения
Использование assertRaises()
в качестве контекстного менеджера позволяет проверять тип и содержание исключения:
with self.assertRaises(SomeException) as cm:
code_that_raises() # Считаем, что здесь "ящик Пандоры" с ошибками
self.assertEqual('Ожидаемое сообщение', str(cm.exception)) # Проверяем, что это именно то исключение, которое мы ожидали
Этот подход оказывается особенно полезным для отлова ошибки "NoneType object is not subscriptable" (обычно случается при попытке обращения к объекту None
, как к последовательности).
Стратегии для старых версий Python
- Python 2.7 и выше: Используйте
assertRaises()
в качестве контекстного менеджера. - Python 2.6 и ниже: Используйте модуль
unittest2
, который расширяет возможности вашего тестового фреймворка, в том числе добавляет использованиеassertRaises()
в качестве контекстного менеджера.
Продвинутые способы работы
В более сложных случаях, связанных с assertRaises()
, может пригодиться функция operator.itemgetter
, позволяющая избегать ошибки TypeError при имитировании индексирования объектов, которые не поддерживают индексацию:
import operator
self.assertRaises(TypeError, operator.itemgetter(0), None) # Itemgetter играет роль охотника за None
Визуализация
Можно представить assertRaises()
как сетку безопасности (🥅), предназначенную для отлова мяча (🏀) определённого типа:
with self.assertRaises(ExpectedException):
throw_ball() # Ожидаем, что сетка поймает баскетбольный мяч.
Когда в сценарий вступает NoneType
, можно сказать, что мяч словно исчез в воздухе (💨):
with self.assertRaises(ExpectedException):
throw_ball() # Мяч не был брошен. Где же мяч?
В этой аналогии судья – это оператор утверждения, проверяющий, пойман ли именно тот мяч, который ожидался:
🥅 Сетка безопасности: "Я ловлю только 🏀!"
💨 Нет мяча: "Подождите, здесь и впрямь ничего нет!"
👨⚖️ Судья: "Ошибка! Ожидали 🏀, а поймали 💨."
Принцип: сетка безопасности (🥅) неэффективна, если в игре только воздух (💨); ей нужен конкретный мяч (🏀), то есть объект, генерирующий исключение.
Настройка вашего assertRaises
Контекст имеет значение
Применение assertRaises()
как контекстного менеджера помогает поддерживать надежность тестов и обеспечивает их стабильное выполнение даже при обновлении версий Python, позволяя точно определить, что именно породило исключение.
Предупреждение ложноположительных результатов
Неправильная работа с assertRaises()
может привести к ложноположительным результатам тестов. Убедитесь, что вызов исключения осуществлён намеренно, и он не связан с другими проблемами, не относящимися к тесту.
Использование слоёв совместимости для Python 2
Для версий Python 2.6 и более ранних модуль unittest2
предоставляет бесплатные обновления возможностей стандартного тестового пакета.
Полезные материалы
- unittest — Фреймворк модульного тестирования — Документация Python 3.12.2 — официальная документация Python по
assertRaises
. - Начало работы с тестированием на Python – Real Python — подробные руководства по первым шагам в модульном тестировании на Python.
- Примеры использования unittest.assertRaises в Python — практические примеры использования
unittest.assertRaises
. - Как правильно формулировать утверждения в тестах — документация pytest — глубокий анализ того, как сделать тестирование максимально эффективным и удобным для поддержки.
- Тестирование в Python 3: Введение в unittest – Mouse Vs Python — вводная статья по модульному тестированию для новичков.
- Встроенные константы — Документация Python 3.12.2 — информация о
NoneType
и его роли в Python.