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

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

Ответы [ 6 ]

7 голосов
/ 23 февраля 2010

Вы можете использовать Ложные объекты для перехвата вызовов методов, которые вы не хотите выполнять. Например. У вас есть класс 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 s в вашем тестовом коде. Еще один способ сделать насмешку состоит в том, чтобы A и MockA реализовали общий интерфейс, скажем, InterfaceA.

Есть тонны насмешливых рамок. См. StackOverflow: фреймворки для Python .

В частности, см .: Google Python Mocking Framework .

5 голосов
/ 23 февраля 2010

Используйте Майкла Фурда Макет в вашем модульном тесте сделайте это:

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")
1 голос
/ 23 февраля 2010

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

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

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, и мой фиктивный объект был успешно введен.

0 голосов
/ 23 февраля 2010

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

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

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

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

0 голосов
/ 23 февраля 2010

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

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

0 голосов
/ 23 февраля 2010

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

Самый чистый способ использует внедрение зависимостей , но Мне не очень , как расширенное получение обезьян , и есть некоторые вещи которые невозможно / трудно сделать, что внедрение зависимостей делает легким.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...