Если вы хотите получить свои тесты Python, чтобы сэкономить ваше время и убедиться, что вас не разбудят посреди ночи болезненные призывы исправить ошибку, продолжайте.
Написание тестов может занять довольно много времени, но если вы знаете, что делаете, и понимаете различные способы имитации кода, вы на правильном пути.
Я быстро расскажу о нескольких различных фиктивных способностях, которые предоставляет Python. Во-первых, вот ссылка на код, обсуждаемый ниже:
GitHub - tzookb / python_mocks_post
Это пути к файлам:
othersrc
other_module.py
src
main.py
some_module.py
test
...
...
other_module.py
def some_method(): return "real result"
def method_with_param(p1, p2): return p1
some_module.py
def some_method(): return "real result"
def method_with_param(p1, p2): return p1
main.py
from src.some_module import some_method import othersrc.other_module as om
def main1(): return some_method()
def main2(): return om.other_method()
def code_with_exception_check(): try: some_method() except Exception as ex: return "error"
def main3(): return om.method_with_param("a_param", "b_param")
def main4(): res = { "a": some_method(), "b": om.other_method() } return res
class HasDependency:
def __init__(self, dependency): self.dependency = dependency def run(self): return self.dependency.run()
А теперь давайте начнем с демонстрации различных способов их тестирования.
Простой макет модуля
def test_simple_patch_in(self):
with patch('src.main.some_method', return_value="mocked resp"):
res = main_module.main1()
self.assertEqual(res, "mocked resp")
Как видите, здесь мы используем «с патчем». Нам нужно передать в качестве первого аргумента «путь» функции, которую мы хотим имитировать. Именованный параметр «return_value» - это один из вариантов, который мы собираемся изучить сегодня. Это просто означает, что при вызове имитируемой функции она вернет то, что мы здесь установили.
Теперь, когда наш испытуемый импортирует some_method, вместо реального кода он получит наш имитируемый объект.
Мокинг модуля и параметры метода
@patch('src.main.some_method', return_value="mocked resp")
def test_simple_patch_on_method(self, some_method_mocked):
res = main_module.main1()
self.assertEqual(res, "mocked resp”)
Таким образом, мы аннотируем наш тестовый метод так же, как в приведенном выше примере. Оба способа делают одно и то же, но таким образом гораздо проще смоделировать несколько объектов без необходимости делать несколько отступов.
Динамическое имитационное действие
@patch('src.main.some_method')
def test_simple_patch_on_method_internal(self, some_method_mocked):
some_method_mocked.return_value="mocked resp"
res = main_module.main1()
self.assertEqual(res, "mocked resp")
То же, что и вышеупомянутая аннотация макета, но здесь мы определяем, как макет будет себя вести или, в частности, что он будет возвращать внутри кода, а не за его пределами.
Несколько моков
@patch('src.main.some_method')
@patch('src.main.om')
def test_simple_patch_on_method_internal(self, om_mock, some_method_mocked):
om_mock.other_method.return_value = "am_mocked"
some_method_mocked.return_value="mocked resp"
res = main_module.main4()
self.assertEqual(res["a"], "mocked resp")
self.assertEqual(res["b"], "am_mocked")
Этот пример просто показывает, как вы можете имитировать несколько функций или модулей. Как видите, порядок аннотаций противоположен порядку переменных внутри списка параметров метода тестирования.
Происходит тестирование исключений
@patch('src.main.some_method')
def test_mock_exception(self, some_method_mocked):
some_method_mocked.side_effect = Exception()
res = main_module.code_with_exception_check()
self.assertEqual(res, "error")
Поскольку мы уже узнали «return_value», здесь у нас есть «side_effect» - здесь вы можете установить Exception, если вы хотите, чтобы ваша имитируемая функция вызывала исключение.
Создайте макет и передайте его как зависимость
def test_dependency(self):
dependency = MagicMock()
dependency.run.return_value = 55
res = (main_module.HasDependency(dependency)).run()
self.assertEqual(res, 55)
Здесь вы можете видеть, что мы не используем «патч», поскольку мы не исправляем какие-либо пути Python. Мы создаем «MagicMock» (именно то, что создает патч), а затем мы можем определить, что нам нужно для этого, и передать его куда угодно.
Глобальное издевательство над объектом
class MainGlobalMockTest(unittest.TestCase): def setUp(self): self.patcher = patch('src.main.some_method') self.some_method_mock = self.patcher.start()
def tearDown(self): self.patcher.stop()
def test_simple(self): self.some_method_mock.return_value="mocked resp" res = main_module.main1() self.assertEqual(res, "mocked resp")
Два новых метода «setUp» и «tearDown» будут выполняться в начале и в конце каждого теста соответственно. Внутри «setUp» мы определяем «патчер», а затем «запускаем» его. Внутри tearDown мы останавливаем его, чтобы он не повлиял на другие тесты.
После вышеизложенного, внутри test_simple все работает точно так же.
Если вы хотите узнать обо мне больше, посетите мой блог
Первоначально опубликовано на x-team.com 11 декабря 2018 г.