В Python, какой хороший шаблон для отключения определенного кода во время модульных тестов?

В общем, я хочу отключить как можно меньше кода, и я хочу, чтобы он был явным: я не хочу, чтобы тестируемый код решал, является ли это тестом или нет, я хочу, чтобы тест сказал, что код "Эй, BTW, я запускаю unit test, не могли бы вы сделать свой звонок на solr, вместо этого вы можете придерживаться того, что вы отправите в solr в этом месте, чтобы я мог его проверить". У меня есть свои идеи, но я не люблю их, я надеюсь, что там будет хороший питонический способ сделать это.

Ответ 1

Используйте Michael Foord Mock в unit test сделайте следующее:

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 methods/attributes
person = Mock(spec=person)
#talkto is some function you are testing
talkTo(person)
#make sure the Person class 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")

Ответ 2

Вы можете использовать Mock objects для перехвата вызовов метода, которые вы не хотите выполнять. Например. У вас есть класс 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.

Есть тонны насмешливых фреймворков. См. fooobar.com/questions/64246/....

В частности, см. Google Python mocking framework.

Ответ 3

Большая проблема, с которой я столкнулась, заключалась в механизме инъекции зависимостей. Я теперь понял эту часть.

Мне нужно импортировать модуль точно так же в обоих местах, чтобы успешно вводить новый код. Например, если у меня есть следующий код, который я хочу отключить:

from foo_service.foo import solr
solr.add(spam)

Я не могу сделать это в своем тестовом бегуне:

from foo import solr
solr = mock_object

интерпретатор python должен обрабатывать модули foo_service.foo и foo как разные записи. Я изменил from foo import solr на более явный from foo_service.foo import solr, и мой макет был успешно введен.

Ответ 4

У вас есть два способа сделать это: или минимальный в случае DI, изменений в исходном коде

самый чистый способ использует инъекция зависимостей, но Я действительно не как обширная обезьяна-патч, и есть некоторые вещи, которые невыполнимы/трудно сделать, что инъекция зависимостей упрощается.

Ответ 5

Обычно, когда что-то подобное возникает, вы используете Monkey Patching (также называемый Duck Punching) для достижения желаемых результатов. Ознакомьтесь с эту ссылку, чтобы узнать больше о Monkey Patching.

В этом случае, например, вы должны перезаписать solr, чтобы просто распечатать вывод, который вы ищете.

Ответ 6

Я знаю, что это типичный пример использования для штучных объектов, но это также старый аргумент... являются объектами Mock вообще или они зла?

Я нахожусь на стороне тех, кто считает, что издевательства зла и будут пытаться вообще не менять тестируемый код. Я даже считаю, что такая необходимость модифицировать проверенный код - это запах кода...

Если вы хотите изменить или перехватить вызов внутренней функции для целей тестирования, вы также можете сделать эту функцию явной внешней зависимостью, установленной во время instanciation, которая будет предоставляться как вашим производственным кодом, так и тестовым кодом. Если вы это сделаете, проблема исчезнет, ​​и вы получите более чистый интерфейс.

Обратите внимание, что при этом не нужно изменять тестируемый код вообще ни внутри, ни при выполнении теста.