#python #unit-testing #pytest
#python #модульное тестирование #pytest
Вопрос:
У меня есть некоторый код, который создает экземпляры из списка классов, который передается ему. Это не может измениться, поскольку список передаваемых ему классов был разработан как динамический и выбирался во время выполнения через файлы конфигурации). Инициализация этих классов должна выполняться тестируемым кодом, поскольку это зависит от факторов, которыми только тестируемый код знает, как управлять (т. Е. он установит конкретные аргументы инициализации). Я довольно тщательно протестировал код, запустив его и вручную просмотрев множество выходных данных. Очевидно, что я нахожусь на том этапе, когда мне нужно добавить несколько правильных тестов unittest, поскольку я доказал свою концепцию самому себе. Следующий пример демонстрирует, что я пытаюсь протестировать:
Я хотел бы протестировать run
метод Foo
класса, определенного ниже:
# foo.py
class Foo:
def __init__(self, stuff):
self._stuff = stuff
def run():
for thing in self._stuff:
stuff = stuff()
stuff.run()
Где один (или несколько) файлов будут содержать определения классов для материала для запуска, например:
# classes.py
class Abc:
def run(self):
print("Abc.run()", self)
class Ced:
def run(self):
print("Ced.run()", self)
class Def:
def run(self):
print("Def.run()", self)
И, наконец, пример того, как это будет связано вместе:
>>> from foo import Foo
>>> from classes import Abc, Ced, Def
>>> f = Foo([Abc, Ced, Def])
>>> f.run()
Abc.run() <__main__.Abc object at 0x7f7469f9f9a0>
Ced.run() <__main__.Abc object at 0x7f7469f9f9a1>
Def.run() <__main__.Abc object at 0x7f7469f9f9a2>
Где список материалов для запуска определяет классы объектов (НЕ экземпляры), поскольку у экземпляров короткий срок службы; они создаются Foo.run() и умирают, когда (или, скорее, вскоре после) функция завершается. Однако я нахожу, что очень сложно придумать понятный метод для тестирования этого кода.
Я хочу доказать, что был вызван run
метод каждого из классов в списке материалов для запуска. Однако из теста у меня нет видимости на Abc
экземпляре, который создает run
метод, поэтому, как это можно проверить? Я не могу исправить импорт, поскольку тестируемый код явно не импортирует класс (в конце концов, ему все равно, какой это класс). Например:
# test.py
from foo import Foo
class FakeStuff:
def run(self):
self.run_called = True
def test_foo_runs_all_stuff():
under_test = Foo([FakeStuff])
under_test.run()
# How to verify that FakeStuff.run() was called?
assert <SOMETHING>.run_called, "FakeStuff.run() was not called"
Комментарии:
1. Если ваш тестируемый код чем-то похож на
Foo
описанный выше, то у васrun()
есть полный контроль над тем, что вы в него передаете, и, следовательно, полная видимость того, что происходит при вызове каждого из ВР методов.2. кстати, я думаю, вы имеете в виду, что
Foo.run()
метод выполняет:stuff = thing(); stuff.run()
3. У вас есть ctrl над
Abc, Ced, Def
кодом? Если вы это сделаете — вы можете обернуть метод run декоратором.4. Я думаю, что пример
FakeStuff
означает, что тест передает все, что ему нравится, вFoo()
Ответ №1:
Кажется, вы правильно понимаете, что можете передавать что угодно в Foo()
, поэтому вы должны иметь возможность что-то регистрировать FakeStuff.run()
:
class Foo:
def __init__(self, stuff):
self._stuff = stuff
def run(self):
for thing in self._stuff:
stuff = thing()
stuff.run()
class FakeStuff:
run_called = 0
def run(self):
FakeStuff.run_called = 1
def test_foo_runs_all_stuff():
under_test = Foo([FakeStuff, FakeStuff])
under_test.run()
# How to verify that FakeStuff.run() was called?
assert FakeStuff.run_called == 2, "FakeStuff.run() was not called"
Обратите внимание, что я изменил ваш оригинал Foo
так, как, я думаю, вы имели в виду. Пожалуйста, поправьте меня, если я ошибаюсь.
Комментарии:
1. Спасибо за подсказку! — Глупо (вероятно, потому, что уже поздно, и я работаю уже долгое время) Я совершенно забыл о том факте, что я могу использовать атрибуты класса…. ОГО!!! Я вроде как думал о том, чтобы пойти по пути использования patch и поразмыслить, как это могло бы работать — я совершенно уверен, что это невозможно.