在Python中重写unittest.TestCase的init算不算非传统?
每当我需要在所有测试开始之前进行一次性初始化,而不是在每个测试之前,我通常会在测试类外面写一个单独的函数来处理这个初始化。我在想,是否可以重写一下 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 个回答
这可以通过使用 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)
注意,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
文档中的类和模块的准备工作部分。
特别是,setUpClass
和tearDownClass
正好做你想要的事情:它们在运行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
。如果你混合了不同类的测试,你可能会看到setUpClass
和tearDownClass
被调用多次。