В целом я хочу отключить как можно меньше код, и я хочу, чтобы он был явным: Я не хочу код, протестированный, чтобы решить, является ли это тестом или нет, я хочу, чтобы тест сказал, что код "эй, BTW, я выполняю модульный тест, может Вы не выполнять свой вызов к solr, вместо этого можете Вы засовывать то, что Вы отправили бы в solr в этом месте, таким образом, я могу проверить его". У меня есть свои идеи, но мне не нравится ни один из них, я надеюсь, что существует хороший pythonic способ сделать это.
Используйте макет Майкла Фурда в своем модульном тесте, сделайте следующее:
from mock import Mock
class Person(object):
def __init__(self, name):
super(Person, self).__init__()
self.name = name
def say(self, str):
print "%s says \"%s\"" % (self.name, str)
...
#In your unit test....
#create the class as normal
person = Person("Bob")
#now mock all of person's methods/attributes
person = Mock(spec=person)
#talkto is some function you are testing
talkTo(person)
#make sure the Person class's say method was called
self.assertTrue(person.say.called, "Person wasn't asked to talk")
#make sure the person said "Hello"
args = ("Hello")
keywargs = {}
self.assertEquals(person.say.call_args, (args, keywargs), "Person did not say hello")
Большая проблема, с которой я столкнулся, заключалась в механике внедрения зависимостей. Теперь я понял эту часть.
Мне нужно импортировать модуль точно так же в обоих местах, чтобы успешно внедрить новый код. Например, если у меня есть следующий код, который я хочу отключить:
from foo_service.foo import solr
solr.add(spam)
Кажется, я не могу сделать это в моем тестовом средстве выполнения:
from foo import solr
solr = mock_object
Интерпретатор python должен обрабатывать модули foo_service.foo
и foo
как разные записи. Я изменил с foo import solr
на более явный из foo_service.foo import solr
, и мой фиктивный объект был успешно внедрен.
У вас есть два способа сделать это: нет или минимальный в случае DI , модификаций для ваш исходный код
Самый чистый способ использует инъекцию зависимостей , но я не очень люблю обширные monkeypatching , и есть некоторые вещи, которые невозможно / сложно сделать, что упрощает внедрение зависимостей .
Я знаю, что это типичный вариант использования имитирующих объектов, но это тоже старый аргумент ... нужны ли имитирующие объекты вообще или они зло ?
Я на стороне тех, кто считает, что насмешки зло , и стараюсь вообще избегать изменения проверяемого кода. Я даже считаю, что такая необходимость изменить проверенный код - это запах кода ...
Если вы хотите изменить или перехватить вызов внутренней функции для целей тестирования, вы также можете сделать эту функцию явной внешней зависимостью, установленной во время создания экземпляра, которая будет предоставляется как вашим производственным кодом, так и тестовым кодом. Если вы это сделаете, проблема исчезнет, и вы получите более чистый интерфейс.
Обратите внимание, что при этом нет необходимости изменять тестируемый код ни внутри, ни во время выполнения теста.
Вы можете использовать Mock объекты для перехвата вызовов методов, которые вы не хотите выполнять.
Например, у вас есть класс A
, в котором вы не хотите, чтобы метод no()
был вызван во время теста.
class A:
def do(self):
print('do')
def no(self):
print('no')
Макетный объект может наследоваться от A
и переопределить no()
, чтобы ничего не делать.
class MockA(A):
def no(self):
pass
Тогда в тестовом коде вы будете создавать объекты MockA
вместо A
ов. Другой способ мокинга заключается в том, что A
и MockA
реализуют общий интерфейс, скажем InterfaceA
.
Существует множество фреймворков для мокинга. См. StackOverflow: Python mocking frameworks.
В частности, см: Google's Python mocking framework.
Обычно, когда возникает что-то подобное, вы используете Monkey Patching (также называемый Duck Punching) для достижения желаемых результатов. Просмотрите эту ссылку, чтобы узнать больше о "обезьяньей заделке".
В этом случае, например, вы перезапишете solr, чтобы он просто выводил нужный вам результат.