mock_object.assert_called_with(arg1, arg2, ...)
или mock_object.assert_called_once_with(arg1, arg2, ...)
. Эти методы проверяют, был ли мок вызван с определенными аргументами.call
для более сложных проверок: Модуль unittest.mock
предоставляет call
, который позволяет проверять последовательность вызовов и аргументы для каждого вызова. Например, mock_object.mock_calls
содержит список всех вызовов, и можно проверять конкретные элементы этого списка с помощью unittest.mock.call
.side_effect
для валидации аргументов: Можно присвоить функции с side_effect
и внутри нее проверить аргументы, используя assert
или выбрасывая исключения, если аргументы не соответствуют ожиданиям. Это позволяет производить более сложные проверки, чем простое сравнение с заданными значениями.ANY
для игнорирования значений аргументов: Если конкретное значение аргумента не важно, можно использовать unittest.mock.ANY
, чтобы проверить только тип аргумента, а не его точное значение.mock_obj.method(1, 'a', kwarg='value')
можно проверить: mock_obj.method.assert_called_with(1, 'a', kwarg='value')
или mock_obj.method.assert_called_with(1, unittest.mock.ANY, kwarg=unittest.mock.ANY)
Проверка аргументов, передаваемых в функции или методы, с использованием мок-объектов — важная часть модульного тестирования, особенно когда нужно убедиться, что тестируемый код взаимодействует с другими частями системы (например, классами, функциями, API) ожидаемым образом. Мок-объекты позволяют заменить реальные зависимости фиктивными, упрощая тестирование и изолируя тестируемый код.
Вот несколько способов проверки аргументов, передаваемых в функции/методы мок-объектов:
1. Использование `mock.call` и `mock.assert_called_with()`/`mock.assert_has_calls()`:
Это наиболее распространенный и рекомендуемый способ.
import unittest
from unittest.mock import Mock, call
class MyClass:
def do_something(self, arg1, arg2):
# Здесь код, который должен вызывать другие функции
pass
class TestMyClass(unittest.TestCase):
def test_do_something_with_args(self):
mock_dependency = Mock()
my_instance = MyClass()
my_instance.dependency = mock_dependency # Предположим, что MyClass использует dependency
# Когда-то в do_something вызывается dependency.some_method(1, 'hello')
# Сделаем вид, что do_something делает это.
my_instance.do_something(10, "world")
mock_dependency.some_method(1, 'hello')
# Проверяем, что dependency.some_method была вызвана с ожидаемыми аргументами
mock_dependency.some_method.assert_called_with(1, 'hello')
# Проверка нескольких вызовов
calls = [call(1, 'hello'), call(2, 'world')]
mock_dependency.some_method.assert_has_calls(calls) # Убедимся, что такие вызовы произошли
2. Использование `side_effect` с функцией проверки:
Этот метод позволяет выполнить произвольную проверку аргументов в момент вызова мок-объекта.
import unittest
from unittest.mock import Mock
class TestMyClass(unittest.TestCase):
def test_do_something_with_side_effect(self):
mock_dependency = Mock()
def check_args(arg1, arg2):
self.assertEqual(arg1, 1)
self.assertEqual(arg2, 'hello')
mock_dependency.some_method.side_effect = check_args
# Сделаем вид, что происходит вызов
mock_dependency.some_method(1, 'hello')
# Если assertion в check_args не выполнится, тест упадет.
3. Использование `call_args` и `call_args_list`:
Эти атрибуты мок-объекта предоставляют доступ к аргументам последних и всех вызовов соответственно.
import unittest
from unittest.mock import Mock
class TestMyClass(unittest.TestCase):
def test_do_something_with_call_args(self):
mock_dependency = Mock()
mock_dependency.some_method(1, 'hello')
mock_dependency.some_method(2, 'world')
# Проверяем аргументы последнего вызова
args, kwargs = mock_dependency.some_method.call_args
self.assertEqual(args, (2, 'world'))
# Проверяем аргументы всех вызовов
call_list = mock_dependency.some_method.call_args_list
self.assertEqual(len(call_list), 2)
args1, kwargs1 = call_list[0]
args2, kwargs2 = call_list[1]
self.assertEqual(args1, (1, 'hello'))
self.assertEqual(args2, (2, 'world'))
Выбор подходящего метода:
Пример более сложной проверки (комбинированный подход):
import unittest
from unittest.mock import Mock, call
class TestMyClass(unittest.TestCase):
def test_complex_argument_check(self):
mock_obj = Mock()
def side_effect_check(data, expected_keys):
self.assertIsInstance(data, dict)
for key in expected_keys:
self.assertIn(key, data)
return True
mock_obj.some_method.side_effect = side_effect_check
data1 = {"name": "Alice", "age": 30}
expected_keys1 = ["name", "age"]
mock_obj.some_method(data1, expected_keys1)
data2 = {"city": "New York", "country": "USA"}
expected_keys2 = ["city"]
mock_obj.some_method(data2, expected_keys2)
# Проверяем, что mock_obj.some_method вызывался 2 раза
self.assertEqual(mock_obj.call_count, 2)
# Проверяем, что side_effect был вызван корректно и assertion внутри него прошла
# если бы assertion упала в side_effect_check - тест бы упал
# Можно также проверить вызовы с assert_has_calls
calls = [
call({"name": "Alice", "age": 30}, ["name", "age"]),
call({"city": "New York", "country": "USA"}, ["city"])
]
mock_obj.some_method.assert_has_calls(calls)
Помните, что цель использования мок-объектов — изолировать тестируемый код и убедиться, что он правильно взаимодействует со своими зависимостями. Выбирайте метод проверки аргументов, который лучше всего соответствует вашим потребностям и обеспечивает наиболее ясное и надежное тестирование.