在Python中重写unittest.TestCase的init算不算非传统?

4 投票
2 回答
4464 浏览
提问于 2025-04-18 10:36

每当我需要在所有测试开始之前进行一次性初始化,而不是在每个测试之前,我通常会在测试类外面写一个单独的函数来处理这个初始化。我在想,是否可以重写一下 unittest.TestCase__init__ 方法,把初始化放在里面。

这是我通常的做法:

import unittest


basket = {}


def initiate():
    """Update Fruit basket - fruits and their ages."""
    fruits = {2: "Apples",
              4: "Grapes",
              3: "Bananas"}
    basket.update(fruits)


class FruitBasket(unittest.TestCase):

    def test_grapes(self):
        """Any grapes in the basket."""
        self.assertIn("Grapes", basket.values())


    def test_rotten(self):
        """Stinky test."""
        for age in basket.keys():
            self.assertLess(age, 4)


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

这个 initiate() 函数和 FruitBasket 测试类是独立的。当这个模块被其他模块导入时,就会出现问题(需要重复手动初始化)。有没有更好的方法在同一个类里实现这个功能呢?我之前没有见过重写 TestCase__init__() 方法。如果这样做不符合 Python 的风格,那我该怎么做呢?而且 setUp() 这个方法不太合适,因为它是在每个测试之前都要调用,而我只想要一次初始化。

2 个回答

0

这可以通过使用 setUpClass 类方法 来更好地实现:

class FruitBasket(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.basket = {2: "Apples",
                      4: "Grapes",
                      3: "Bananas"}

    def test_grapes(self):
        """Any grapes in the basket."""
        self.assertIn("Grapes", self.basket.values())

    def test_rotten(self):
        """Stinky test."""
        for age in self.basket.keys():
            self.assertLess(age, 4)
6

注意,TestCase.__init__在构建测试套件和运行测试时,用来选择要运行的测试方法。实际上,当你想手动创建一个TestSuite来运行测试,而不使用更高级的测试运行器接口时,你可以这样做:

import unittest


class MyTestCase(unittest.TestCase):
    def test_one(self):
        print("One")
    def test_two(self):
        print("Two")


suite = unittest.TestSuite([MyTestCase('test_one'), MyTestCase('test_two')])

suite.run(unittest.TestResult())

看!你为每个测试都创建了一个MyTestCase实例!

记住,当你使用更高级的接口来运行测试时,它只是隐藏了像上面那样构建TestSuite的步骤。

这已经说明了你不应该重写__init__,因为它会为每个测试调用一次。

不过,unittest模块提供了机制,可以为类甚至模块添加一些准备工作。你可以查看unittest文档中的类和模块的准备工作部分。

特别是,setUpClasstearDownClass正好做你想要的事情:它们在运行TestCase子类的测试之前和之后各被调用一次


注意,setUpClass可能会被调用次。不过,这种情况只会在手动构建TestSuite时发生。例如,以下代码:

import unittest


class MyTestCase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print('MyTestCase.setUpClass')
    def test_one(self):
        print("One")
    def test_two(self):
        print("Two")

class MyTestCase2(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print('MyTestCase2.setUpClass')
    def test_three(self):
        print('Three')



suite = unittest.TestSuite([MyTestCase('test_one'), MyTestCase2('test_three'), MyTestCase('test_two')])

suite.run(unittest.TestResult())

会产生以下输出:

MyTestCase.setUpClass
One
MyTestCase2.setUpClass
Three
MyTestCase.setUpClass
Two

TestSuite按顺序执行测试。每当它发现测试来自不同的类时,它会先调用“当前”类的tearDownClass,然后再为下一个类调用setUpClass。如果你混合了不同类的测试,你可能会看到setUpClasstearDownClass被调用多次。

撰写回答