2013-04-05 4 views
0

У меня есть программа с несколькими очень похожими классами:Как UnitTest несколько подобных классов

class BlackBox1(): 
    def calc(self, a, b): 
     return a + b 

class BlackBox2(): 
    def calc(self, a, b): 
     return a * b 
... 

Теперь я хочу написать UnitTests для всех этих классов. Конечно, я могу написать отдельные тесты для каждого Blackbox. Во всяком случае, так как каждый черный ящик имеет точно такой же метод calc(a, b) для тестирования, интересно, если есть что-то вроде «лучшей практики», чтобы автоматически давать уроки и ожидаемые результаты в абстрактном рамки теста, что-то вроде

import unittest 
class TestAbstractBox(unittest.TestCase): 
    def setUp(self): 
     self.box = blackbox() 
     self.param_a = a 
     self.param_b = b 
     self.expected_result = result 

    def test_calc_method(self): 
     real_result = self.box.calc(self.param_a, self.param_b) 
     self.assertEqual(real_result, self.expected_result, 
         "{0} gives wrong result".format(self.box.__class__)) 

TAbstractTest = unittest.defaultTestLoader.loadTestsFromTestCase(TestAbstractBox) 

Есть ли способ передать {"blackbox": Blackbox1, "a": 3, "b": 5, "result": 8} и {"blackbox": Blackbox2, "a": 4, "b": 7, "result": 28} классу TestAbstractBox, чтобы не иметь несколько раз тот же код, но иметь простой способ протестировать новые Blackboxes?

ответ

0

Я думаю, я нашел решение, в основном следующие this answer:

import unittest 

class BlackBox1(): 
    def calc(self, a, b): 
     return a + b 

class BlackBox2(): 
    def calc(self, a, b): 
     return a * b 

class TestBlackBox1(unittest.TestCase): 
    test_params = {"blackbox": BlackBox1, "a": 3, "b": 5, "result": 8} 
    def setUp(self): 
     self.box = self.test_params["blackbox"]() 
     self.param_a = self.test_params["a"] 
     self.param_b = self.test_params["b"] 
     self.expected_result = self.test_params["result"] 

    def test_calc_method(self): 
     real_result = self.box.calc(self.param_a, self.param_b) 
     self.assertEqual(real_result, self.expected_result, 
         "{0} gives wrong result: {1} instead of {2}".format 
         (self.box.__class__, real_result, self.expected_result)) 

class TestBlackBox2(TestBlackBox1): 
    test_params = {"blackbox": BlackBox2, "a": 4, "b": 7, "result": 28} 

TBB1 = unittest.defaultTestLoader.loadTestsFromTestCase(TestBlackBox1) 
TBB2 = unittest.defaultTestLoader.loadTestsFromTestCase(TestBlackBox2) 

В любом случае, anks для других ответов и жаль, что не нашел этого в первую очередь.

+0

И как это лучше, просто просто удерживая массив dicts в 'setUp'? Это только усложняет ваш тест. – freakish

+0

Просто для того, чтобы назвать самые важные: если нужны дополнительные тесты для некоторых черных ящиков, их можно легко добавить в класс, если у меня будет более одного теста, ваш ответ будет требовать каждый раз, когда цикл for, я вижу только первый неудачный тест в test_calc_method (поэтому, если несколько ящиков сломаны, я получаю их только шаг за шагом). Поскольку это основа, другие люди могут добавлять дополнительные черные ящики, я могу дать им простой способ проверить свои поля. Мне также нравится ваш пример, но для моего случая это работает лучше и выглядит более объективно ориентированным. –

+0

Уверен, что вам больше подходит. – freakish

1

Вы можете просто добавить эти классы setUp:

class TestAbstractBox(unittest.TestCase): 
    def setUp(self): 
    self.boxes = [Blackbox1(), Blackbox2()] 
    self.param_a = a 
    self.param_b = b 
    self.expected_result = result 

    def test_calc_method(self): 
    for box in self.boxes: 
     real_result = self.box.calc(self.param_a, self.param_b) 
     self.assertEqual(real_result, self.expected_result, 
      "{0} gives wrong result".format(self.box.__class__)) 

EDIT Version 2:

class TestAbstractBox(unittest.TestCase): 
    def setUp(self): 
    self.boxes = [ 
     { "blackbox":Blackbox1(), "a":a1, "b":b1, "expected_result":result1 }, 
     { "blackbox":Blackbox2(), "a":a2, "b":b2, "expected_result":result2 }, 
    ] 

    def test_calc_method(self): 
    for box in self.boxes: 
     real_result = box["blackbox"].calc(box["a"], box["b"]) 
     self.assertEqual(real_result, box["expected_result"], 
      "{0} gives wrong result".format(box["blackbox"].__class__)) 
+0

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

+0

@SebastianWerk Ну, вы можете сохранить один массив словарей в формате, который вы нам показали: '{" blackbox ": Blackbox1," a ": 3," b ": 5," result ": 8}'. Вы все равно должны передать эти параметры в какой-то момент. – freakish

+0

@ freakish Я сосать в unittesting, и я делаю много ката, чтобы поправляться, это действительно лучший подход для unittest? Кроме того, если он хочет иметь более одного теста, это не позволит методу setUp иметь как черные ящики, прикрепленные к 'self', даже если он просто хочет проверить что-то еще, как' Blackbox() '' '' '' '' пример? Поскольку 'setUp()' работает для каждого теста, правильно? Кстати, это вопрос не критика! : D –

0

Я не думаю, что это такой хороший подход.

Метод тестирования должен выполнять ровно один тест, а не цикл через несколько вызовов assert*. В противном случае в случае сбоя вы не сможете легко сказать, какой тест не прошел. В конце концов, это все дело в том, чтобы делать unittests.

Кроме того, вы не должны рисковать нарушать существующие тесты, добавляя новые варианты для новых классов или рефакторинг существующих тестов в некоторые распространенные методы.

В случае нескольких классов с идентичными методами (подумайте о реализации общего интерфейса в нескольких производных классах) может оказаться полезным поставить все тесты для общих свойств интерфейса в один базовый класс, а затем вывести индивидуальные тестовые классы для каждого тестируемого класса. Затем эти производные классы применяют подходящие методы setUp и tearDown. Они также могут добавить дополнительные тестовые примеры, относящиеся к тестируемому классу.

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