使用unittest在Python中处理多个测试用例的合适结构

3 投票
3 回答
1521 浏览
提问于 2025-04-15 23:25

我在研究 unittest 这个包,但不太确定在为同一个方法写很多测试用例时,应该怎么组织我的测试用例。假设我有一个 fact 函数,它用来计算一个数字的阶乘;这样写的测试文件可以吗?

import unittest

class functions_tester(unittest.TestCase):
    def test_fact_1(self):
        self.assertEqual(1, fact(1))
    def test_fact_2(self):
        self.assertEqual(2, fact(2))
    def test_fact_3(self):
        self.assertEqual(6, fact(3))
    def test_fact_4(self):
        self.assertEqual(24, fact(4))
    def test_fact_5(self):
        self.assertFalse(1==fact(5))
    def test_fact_6(self):
        self.assertRaises(RuntimeError, fact, -1)
        #fact(-1)

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

我觉得为一个方法写这么多测试方法有点乱。我想只写一个测试方法,然后把很多基本的测试用例放进去(比如 4! == 24,3! == 6,5! == 120,等等),但是 unittest 不允许这样做。

在这种情况下,组织测试文件的最佳方式是什么呢?

提前谢谢你的帮助。

3 个回答

0

你提到你在研究 unittest,但不妨考虑使用 nose 测试。因为 nose 可以让你通过编程的方式生成独立的测试案例。

如何在Python中生成动态(参数化)单元测试? (回答)

5

我觉得你现在的做法 大体上 是可以的(不过接着往下看)。

你可以像interjay建议的那样,使用循环(顺便说一下,这样只算一个测试,因为unittest模块是根据函数的数量来计算的,而不是根据断言的数量)。不过我猜你不会试着去测试 每一个 数字,甚至不会测试一个非常大的范围内的所有数字。所以用循环其实帮不了你太多,特别是在测试的时候,你应该尽量做到明确。

话虽如此,你应该测试一小部分连续的数字(比如说,1到5),然后尝试找出可能的边界情况和失败点。比如,测试10、100、1000(也就是改变数量级),负数,零等等。

顺便提一下,注意你最后两个测试。第一个测试的意义不大。fact(5)和很多数字是不同的(实际上是无穷多个数字)。测试正确的情况,测试错误的情况并没有什么帮助。

def test_fact_5(self):
    self.assertFalse(1==fact(5))

第二个测试的名字起得不好:“test_fact_6”让我以为你在测试fact(6)。你应该把它命名为“test_fact_minus_one”,或者至少是“test_fact_negative_number”。

def test_fact_6(self):
    self.assertRaises(RuntimeError, fact, -1)

测试的命名非常重要,无论是在调试错误的时候,还是在你回头查看测试作为文档的时候。

6

你可以把这些检查放在一个循环里:

def test_fact(self):
    tests = [(1,1), (2,2), (3,6), (4,24), (5,120)]
    for n,f in tests:
        self.assertEqual(fact(n), f)

撰写回答