2015-02-09 2 views
0

Я пытаюсь понять Python Mock, чтобы лучше тестировать мой код. В прошлом я не проводил много модульного тестирования, но хочу подчеркнуть, что он продвигается вперед. «With mock.patch (« что-то ») как« mock »: синтаксис кажется действительно удобным для насмешек, которые мой код использует. Это было особенно удобно для имитации запросов к базе данных или API.Хороший подход к насмешкам объектов в Python?

Однако, я замечаю, что количество тестов, которые я пишу, растет, так же как и дублирование в моих тестах. Если у меня есть несколько классов, используемых в моем классе (MyClass ниже), которые нужно издеваться, мне нужно высмеять их для нескольких тестов, даже если они не используются непосредственно для конкретного теста. Например:

with context("my test"): 
    with it('responds true'): 
    with mock.patch('lib.mymodule.ClassA') as MockClassA: 
     with mock.patch('lib.mymodule.ClassB') as MockClassB: 
     with mock.patch('lib.mymodule.ClassC') as MockClassC: 
      MockClassA.return_value = "bogus result" 
      f = MyClass("host", "user", "password") 
      self.assertEqual(f, "bogus result") 

В этом случае MockClassA, B, C и может говорить с базой данных или сделать API вызов, который я на самом деле не нужно делать во время тестирования. Но поскольку мой класс использует каждый, мне нужно высмеять их все для всех тестов. Есть лучший способ сделать это?

EDIT: исправлен мой код, чтобы отразить, что я использую Mamba для модульного тестирования. Прошу прощения за то, что не упоминал об этом вначале.

+0

Должно ли это быть 'class TestHostRecordCreation (unittest.TestCase):'? – mgilson

+2

Ваш вопрос кажется похожим на вопрос, заданный по адресу http://stackoverflow.com/q/28223321/748858 ... В этом ответе я покажу, как перемещать патчи в методе setUp, или использовать его как декоратор класса. Посмотрите там и посмотрите, поможет ли вам ваша проблема. – mgilson

+0

Прошу прощения, я печатал это от руки. Я на самом деле использую Mamba, а не unittest. – blindsnowmobile

ответ

1

Не только случай, если patch documentation начинается с

пластырем() действует как функция декоратор, класс декоратор ....

Использование патча декоратора является одним из лучших способов для повышения удобочитаемости и простоты. Ваше дело станет

def TestHostRecordCreation(self): 
    @mock.patch('lib.mymodule.ClassC') 
    @mock.patch('lib.mymodule.ClassB') 
    @mock.patch('lib.mymodule.ClassA') 
    def test_create_record(self, MockClassA, MockClassB, MockClassC): 
     f = MyClass("host", "user", "password") 
     self.assertEqual(f, "bogus result") 

Кроме того, если вы хотите сделать те же патчи для всех тестовых примеров, вы можете украсить класс, а не отдельные методы. As documented here украшают класс одним из patch декораторами действуют как patch все методы, начинающиеся с patch.TEST_PREFIX. В вашем случае мы используем значение по умолчанию для patch.TEST_PREFIX и мы можем написать:

@mock.patch('lib.mymodule.ClassC') 
@mock.patch('lib.mymodule.ClassB') 
@mock.patch('lib.mymodule.ClassA') 
def TestHostRecordCreation(self): 
    def test_A(self, MockClassA, MockClassB, MockClassC): 
     f = MyClass("host", "user", "password") 
     self.assertEqual(f, "bogus result") 

    def test_B(self, MockClassA, MockClassB, MockClassC): 
     f = MyClass("myhost", "myuser", "password") 
     self.assertEqual(f, "other bogus result") 

Наконец, вы можете использовать patch.multiple залатать набор атрибутов. В том, что конкретные синтетические случае швы очень мощные, но в реальном слове случаев его использование очень редко:

@mock.patch.multiple('lib.mymodule', ClassA=mock.DEFAULT, ClassB=mock.DEFAULT, ClassC=mock.DEFAULT) 
def TestHostRecordCreation(self): 
    def test_A(self, MockClassA, MockClassB, MockClassC): 
     f = MyClass("host", "user", "password") 
     self.assertEqual(f, "bogus result") 

    def test_B(self, MockClassA, MockClassB, MockClassC): 
     f = MyClass("myhost", "myuser", "password") 
     self.assertEqual(f, "other bogus result") 

Рассмотрим использование setUp() и tearDown(), если вам нужно создать объекты, которые нужны для многих тестов (каждый каркас модульного теста есть что-то подобное). Вы можете использовать setUp() и tearDown(), чтобы начать и прекратить контексты патчей, но мой вкус в том, что декораторы и with контекст более читабельны.

+0

Спасибо, это полезно. Я на самом деле использую Mamba, о котором я забыл упомянуть в своем оригинальном посте (не unittest). Есть ли способ применить это к Mamba, или, на ваш взгляд, стоит переключиться на unittest? – blindsnowmobile

+0

@blindsnowmobile Конечно. В [упомянутой документации] (https://docs.python.org/3/library/unittest.mock.html#test-prefix) ясно, что ваш класс может быть любого типа (а не просто 'unittest.TestCase') , Декоратор будет просто применять декораторы ко всем функциям, где имя начинается с значения 'patch.TEST_PREFIX'; Значение по умолчанию - 'test', но вы можете изменить его по своему усмотрению. Я обновлю свой ответ, чтобы удалить ссылки 'unittest' и сделать его более общим. –

+0

@blindsnowmobile Обновлено –

Смежные вопросы