Модульное тестирование интерфейсов в Python - PullRequest
6 голосов
/ 26 мая 2010

В настоящее время я изучаю python перед подготовкой к уроку летом и начал с реализации различных типов куч и структур данных, основанных на приоритетах.

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

Мне интересно, возможно ли сделать что-то подобное ..

suite = HeapTestSuite(BinaryHeap())
suite.run()
suite = HeapTestSuite(BinomialHeap())
suite.run()

То, что я сейчас делаю, просто кажется ... неправильным (множественное наследование? ACK!) ..

class TestHeap:

    def reset_heap(self):
        self.heap = None

    def test_insert(self):
        self.reset_heap()
        #test that insert doesnt throw an exception...
        for x in self.inseq:
            self.heap.insert(x)


    def test_delete(self):
        #assert we get the first value we put in
        self.reset_heap()
        self.heap.insert(5)
        self.assertEquals(5, self.heap.delete_min())

        #harder test. put in sequence in and check that it comes out right
        self.reset_heap()
        for x in self.inseq:
            self.heap.insert(x)

        for x in xrange(len(self.inseq)):
            val = self.heap.delete_min()
            self.assertEquals(val, x)

class BinaryHeapTest(TestHeap, unittest.TestCase):
    def setUp(self):
        self.inseq = range(99, -1, -1)
        self.heap = BinaryHeap()

    def reset_heap(self):
        self.heap = BinaryHeap()

class BinomialHeapTest(TestHeap, unittest.TestCase):
    def setUp(self):
        self.inseq = range(99, -1, -1)
        self.heap = BinomialHeap()

    def reset_heap(self):
        self.heap = BinomialHeap()


if __name__ == '__main__':
    unittest.main()

Ответы [ 3 ]

4 голосов
/ 27 мая 2010

Мне лично нравится поколение тестов на нос больше для такого рода вещей. Я бы тогда написал так:

# They happen to all be simple callable factories, if they weren't you could put
# a function in here:
make_heaps = [BinaryHeap, BinomialHeap]

def test_heaps():
    for make_heap in make_heaps:
        for checker in checkers: # we'll set checkers later
            yield checker, make_heap

def check_insert(make_heap):
    heap = make_heap()
    for x in range(99, -1, -1):
        heap.insert(x)

# def check_delete_min etc.

checkers = [
    value
    for name, value in sorted(globals().items())
    if name.startswith('check_')]
2 голосов
/ 26 мая 2010

Почему бы просто не использовать псевдоним для класса, который вы хотите проверить? Вы можете написать свой тестовый класс, ссылаясь на фальшивый класс HeapImpl, а затем назначить ему конкретную реализацию перед каждым запуском теста:

class TestHeap(unittest.TestCase):
    def setUp(self):
        self.heap = HeapImpl()
    #test cases go here

if __name__ == '__main__'
    suite = unittest.TestLoader().loadTestsFromTestCase(TestHeap)
    heaps = [BinaryHeap, BinomialHeap]
    for heap in heaps:
        HeapImpl = heap
        unittest.TextTestRunner().run(suite)

Пока они соответствуют интерфейсу, который вы используете в наборе тестов, это должно работать нормально. Кроме того, вы можете легко протестировать столько реализаций, сколько захотите, просто добавьте их в список heaps.

1 голос
/ 26 мая 2010

Я не думаю, что приведенная выше схема ужасна, но множественное наследование, конечно, не идея.

Полагаю, причина того, что TestHeap не может быть подклассом TestCase, заключается в том, что он будет автоматически выбран и запущен в качестве теста, не зная, что его нужно разделить на подклассы.

Я справился с этой проблемой двумя другими способами:

  1. Вместо того, чтобы добавлять функции test_, используйте методы записи, которые не будут автоматически выбраны, а затем добавьте test () к каждому из ваших подклассов. Очевидно, не идеально.
  2. Переписан юнит-тест, чтобы не сосать, что позволяет установить базовый класс __test__ = False. (См. Свидетельство )
...