在Python中对接口进行单元测试

6 投票
3 回答
3370 浏览
提问于 2025-04-15 23:12

我现在正在学习Python,为了准备夏天的课程,我开始实现不同类型的堆和基于优先级的数据结构。

我开始为这个项目写单元测试,但在创建一个只测试接口而不关心实际实现的通用单元测试时遇到了困难。

我在想是否可以做到这样的事情……

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

我现在的做法感觉……不太对劲(多重继承?天哪!)……

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 个回答

1

我觉得上面的做法还不错,但多重继承确实不是最理想的选择。

我想你不能直接让TestHeap成为TestCase的子类,是因为它会被自动识别为测试用例,然后就会被运行,而不知道它其实需要被当作子类来处理。

我有两种其他的解决方法:

  1. 与其添加以test_开头的函数,不如写一些不会被自动识别的方法,然后在每个子类中添加test()。显然,这也不是最好的办法。
  2. 重写了unittest,使其更好用,允许在基类中设置__test__ = False的选项。(可以查看Testify
2

为什么不直接给你想测试的类起个别名呢?你可以写一个测试类,里面引用一个假的 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 列表中就行。

4

我个人比较喜欢nose测试生成,因为它更适合这种情况。然后我会这样写:

# 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_')]

撰写回答