如何在pyUnit中创建类作用域的测试固件?

2 投票
2 回答
1226 浏览
提问于 2025-04-16 11:05

我正在进行Mercurial集成的单元测试,创建了一个测试类。在这个类的setUp方法里,我会创建一个包含文件的仓库和这个仓库的一个克隆,然后在tearDown方法里把它们删除。

你可以想象,这样做会很耗性能,尤其是当我需要为每个测试单独执行这些操作时。
所以我想要做的是,在加载这个类的时候就创建好文件夹并初始化Mercurial,这样每个TestCase类中的单元测试都可以使用这些仓库。然后在所有测试运行完后,我再把它们删除。这样,我的setUptearDown方法只需要确保在每个测试之间这两个仓库的状态是一样的。

简单来说,我想要的是一个python版本的JUnit中的@BeforeClass@AfterClass注解。

2 个回答

1

来自 Python unittest 文档 的内容:

setUpClass() :

这是一个在每个测试类中的测试开始之前会被调用的方法。setUpClass 方法会接收这个类作为唯一的参数,并且需要用 classmethod() 装饰器来标记:

@classmethod
def setUpClass(cls):
...

这个功能在 2.7 版本中新增。

tearDownClass() :

这是一个在每个测试类中的测试结束后会被调用的方法。tearDownClass 方法同样会接收这个类作为唯一的参数,并且也需要用 classmethod() 装饰器来标记:

@classmethod
def tearDownClass(cls):
    ...
2

我现在通过继承TestSuite类来实现这个功能,因为标准的加载器会把所有的测试方法放在一个TestCase实例中,然后把它们组合成一个TestSuite。我让TestSuite调用第一个TestCase的before()和after()方法。当然,这意味着你不能在TestCase对象中初始化任何值,但你可能还是想在setUp中做这件事。

这个TestSuite看起来是这样的:

class BeforeAfterSuite(unittest.TestSuite):
    def run(self, result):
        if len(self._tests) < 1:
            return unittest.TestSuite.run(self, result)

        first_test = self._tests[0]
        if "before" in dir(first_test):
            first_test.before()
        result = unittest.TestSuite.run(self, result)
        if "after" in dir(first_test):
            first_test.after()
        return result

为了更细致地控制,我还创建了一个自定义的TestLoader,确保BeforeAfterSuite只用来包装测试方法的TestCase对象,结构如下:

class BeforeAfterLoader(unittest.TestLoader):
    def loadTestsFromTestCase(self, testCaseClass):
        self.suiteClass = BeforeAfterSuite
        suite = unittest.TestLoader.loadTestsFromTestCase(self, testCaseClass)
        self.suiteClass = unittest.TestLoader.suiteClass
        return suite

这里可能缺少的是一个try/except块,用来处理before和after方法可能出现的错误,这样可以让所有的测试用例都失败或者其他处理。

撰写回答