Ответ
В библиотеке unittest.mock объекты Mock и MagicMock записывают все обращения к ним. Для проверки этих обращений используются специальные методы-утверждения (assertions).
Настройка поведения мока
Перед проверкой вызовов мок обычно настраивают:
mock.return_value: Атрибут, который определяет значение, возвращаемое при вызове мока.mock.side_effect: Позволяет задать более сложное поведение: выбросить исключение, вернуть разные значения при последовательных вызовах или выполнить функцию.
Проверка вызовов (Assertions)
Основные методы для проверки взаимодействия с моком:
mock.assert_called(): Проверяет, что мок был вызван хотя бы один раз.mock.assert_called_once(): Проверяет, что мок был вызван ровно один раз.mock.assert_not_called(): Проверяет, что мок не вызывался ни разу.mock.assert_called_with(*args, **kwargs): Проверяет, что последний вызов мока был с указанными аргументами.mock.assert_called_once_with(*args, **kwargs): Объединяетassert_called_once()иassert_called_with().mock.assert_any_call(*args, **kwargs): Проверяет, был ли хотя бы один вызов с указанными аргументами среди всех вызовов.mock.call_count: Целочисленный атрибут, содержащий количество вызовов мока.
Практический пример:
Предположим, мы тестируем функцию, которая использует внешний API для отправки уведомлений.
# Код, который мы тестируем
def send_notification(api_client, user_id, message):
if len(message) > 0:
api_client.send(user_id, text=message)
# Тест для этого кода
from unittest.mock import Mock
import unittest
class TestNotification(unittest.TestCase):
def test_send_notification_with_message(self):
# 1. Arrange: Создаем мок для API клиента
mock_api = Mock()
# 2. Act: Вызываем нашу функцию с моком
send_notification(mock_api, 123, "Hello!")
# 3. Assert: Проверяем, что метод .send() был вызван один раз
# и с правильными аргументами.
mock_api.send.assert_called_once_with(123, text="Hello!")
def test_send_notification_with_empty_message(self):
# 1. Arrange
mock_api = Mock()
# 2. Act
send_notification(mock_api, 123, "")
# 3. Assert: Проверяем, что метод .send() не вызывался
mock_api.send.assert_not_called()
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False) Ответ 18+ 🔞
Ага, смотри, вот тебе про эти ваши моки в питоне. Библиотека unittest.mock — это такая штука, которая записывает всё, что с ней делают, как стукач на допросе. А потом ты можешь прийти и спросить: «Ну что, вызывали тебя? А с какими аргументами?» И она тебе всё выложит, сука.
Как этого подопытного настроить
Перед тем как проверять, что он там наговорил, его обычно готовят:
mock.return_value: Говоришь ему: «Слушай, когда тебя вызовут, ты верни вот эту хуйню». И он возвращает.mock.side_effect: А это уже поинтереснее. Тут можно устроить полный пиздец: заставить его кидать исключения, каждый раз возвращать разное или вообще выполнить какую-то функцию. Полный контроль, блядь.
А теперь лови его за язык (Assertions)
Вот основные вопросы, которые ты можешь ему задать после того, как он поработал:
mock.assert_called(): «Тебя вообще вызывали?» — «Да, вызывали, ёпта».mock.assert_called_once(): «Тебя вызывали ровно один раз?» — «Один, блядь, больше не надо».mock.assert_not_called(): «Тебя не трогали?» — «Нет, я чистый, как слеза младенца».mock.assert_called_with(*args, **kwargs): «А в последний раз с какими аргументами тебя дернули?» — проверяет последний вызов.mock.assert_called_once_with(*args, **kwargs): Комбо! «Тебя вызвали один раз и с такими-то параметрами?»mock.assert_any_call(*args, **kwargs): «А вообще, среди всех твоих похождений, был вызов с вот этими штуками?»mock.call_count: Просто посмотреть счётчик, блядь. Цифра. Сколько раз его ебали.
Ну и пример, чтобы не быть просто болтуном:
Допустим, у нас есть функция, которая шлёт уведомления через какой-то внешний API. Нам нужно проверить, что она это делает правильно, но реальный API не дергать — он же, блядь, может сдохнуть, или деньги счёт списать.
# Код, который мы тестируем
def send_notification(api_client, user_id, message):
if len(message) > 0:
api_client.send(user_id, text=message)
# Тест для этого кода
from unittest.mock import Mock
import unittest
class TestNotification(unittest.TestCase):
def test_send_notification_with_message(self):
# 1. Arrange: Лепим муляж API-клиента
mock_api = Mock()
# 2. Act: Запускаем нашу функцию, подсунув ей муляж
send_notification(mock_api, 123, "Hello!")
# 3. Assert: А теперь проверяем, не наврал ли муляж.
# Говорим: «Метод .send() должен был вызваться один раз с вот этими параметрами».
mock_api.send.assert_called_once_with(123, text="Hello!")
def test_send_notification_with_empty_message(self):
# 1. Arrange: Опять муляж
mock_api = Mock()
# 2. Act: Шлём пустое сообщение
send_notification(mock_api, 123, "")
# 3. Assert: А тут метод .send() вообще не должен был дергаться.
# Если дернулся — значит, наша функция ебнулась.
mock_api.send.assert_not_called()
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
Вот и вся магия. Создал муляж, подсунул, проверил, что он сделал то, что нужно. Никаких лишних движений, всё чисто и под контролем. Красота, ёпта.