Class Python 单元测试中的固定装置

Class fixtures in Python's unittest

我正在尝试理解 Python 的 unittest 模块中的 class fixtures。为了加快测试速度,我想在每个 class 中只创建一个 'expensive' 连接对象。乍一看,我想到了这样做:

import unittest
import rethinkdb as r

class TestRethink(unittest.TestCase):
    conn = r.connect('localhost', 28016)

    def test_table_list(self):
        r.table_list().run(self.conn)

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

然而,从文档看来,应该这样做:

import unittest
import rethinkdb as r

class TestRethink(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.conn = r.connect('localhost', 28016)

    def test_table_list(self):
        r.table_list().run(self.conn)

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

这两种方法有什么区别?在这两种情况下,RethinkDB 连接对象都是一个 class 变量,不是吗?

What is the difference between the two approaches? In both cases the RethinkDB connection object is a class variable, isn't it?

主要区别在于执行范围和顺序,class 变量将在加载 class 本身时定义和执行,而 setupClass() 方法将被调用通过 unittest 在 运行 你的测试时,class 将被实例化(它的行为有点像构造函数)。

在您的最小示例中,这应该没什么区别,但是如果您将 class 替换为您的 TestRethink class,那么它就会。因为在第一种情况下,您将为所有子 class 共享一个数据库连接,而对于 setup/teardown,每个子class.[=14= 将有一个连接]

差异 #1

关于第一种方法。 connTestRethink class 的 class 成员。

在第二种方法(unittest 方法)中,TestRethink 的每个子class 将有其自己的不同连接,这意味着每个 TestCase class 将有自己的连接实例。

使用 @classmethod 时的区别示例:

class A(object):
    @classmethod
    def setup(cls):
        cls.conn = 'my connection for %r' % cls

class B(A):
    pass

class C(A):
    pass


# setup the tests
B.setup()
C.setup()

b = B()
c = C()
print(b.conn)
print(c.conn)

此代码输出:

my connection for <class '__main__.B'>
my connection for <class '__main__.C'>

差异 #2

unittest 将在设置子 classes 期间处理异常,并将打印有关失败的更多详细信息,这将更容易调试测试失败的原因。

我建议使用 unittest 方法或尝试 pytest 模块,它有很好的 API 来处理固定装置。