如何在有全局夹具的情况下将Python单元测试转换为py.test?

6 投票
1 回答
11743 浏览
提问于 2025-04-18 13:56

我有一套单元测试,是用Python的unittest模块写的。它们使用了setUpModule()这个函数来加载一个全局变量,这个变量包含了运行测试所需的共享“东西”(包括一些HTTP会话)。

当我用unittest运行我的测试时,一切正常。但用py.test运行时,就失败了。

我稍微修改了一下,试着用旧版的pytest的fixture函数(这些函数的名字和unittest的不一样)来运行。这样做是成功了,但只有在不使用多线程的情况下才行,而我其实是想用多线程的。

文档里的例子对我来说没什么用,因为我有大约20个类(unittest.TestCase),每个类里面有10个测试。显然,我不想在每个测试里都加一个新的参数。

到目前为止,我一直使用类里的setUp()方法来加载共享的字典,然后在每个测试中使用它。

#!/usr/bin/env python
# conftest.py
@pytest.fixture(scope="session")
def manager():
    return { "a": "b"}

现在是测试的部分:

#!/usr/bin/env python
# tests.py

class VersionTests(unittest.TestCase):

    def setUp(self):
        self.manager = manager

    def test_create_version(self):
        # do something with self.manager
        pass

请记住,我需要一个能在多线程下工作的解决方案,而且只调用一次fixture。

1 个回答

6

pytest 确实可以运行 unittest 的测试,这在 支持 unittest.TestCase / 夹具集成 的文档中有说明。不过,有一点需要注意的是,直接使用 pytest 的函数参数夹具 并不推荐:

虽然 pytest 支持通过测试函数的参数接收夹具,但 unittest.TestCase 的方法不能直接接收夹具函数的参数,因为这样做可能会影响运行一般的 unittest.TestCase 测试套件的能力。

假设我们有一个测试模块,像这样使用标准的 unittest 初始化功能:

# test_unittest_tests.py (for the sake of clarity!)
import unittest

manager = None

def setUpModule():
    global manager
    manager = {1: 2}

class UnittestTests(unittest.TestCase):
    def setUp(self):
        self.manager = manager

    def test_1_in_manager(self):
        assert 1 in self.manager

    def test_a_in_manager(self):
        assert 'a' in self.manager

当用 unittest 运行时,会产生以下输出:

$ python -m unittest -v test_unittest_tests
...
test_1_in_manager (test_unittest_tests.UnittestTests) ... ok
test_a_in_manager (test_unittest_tests.UnittestTests) ... FAIL
...

test_a_in_manager 按预期失败了,因为在 manager 目录中没有 'a' 这个键。

我们设置了一个 conftest.py 文件,为这些测试提供了作用域的 pytest 夹具。比如这样做,不会破坏标准的 unittest 行为,也不需要对测试进行任何修改,使用 pytest 的自动使用功能

# conftest.py
import pytest

@pytest.fixture(scope='session', autouse=True)
def manager_session(request):
    # create a session-scoped manager
    request.session.manager = {'a': 'b'}

@pytest.fixture(scope='module', autouse=True)
def manager_module(request):
    # set the sessions-scoped manager to the tests module at hand
    request.module.manager = request.session.manager

pytest 运行测试(使用 pytest-xdist 进行并行化),会产生以下输出:

$ py.test -v -n2
...
[gw1] PASSED test_unittest_tests.py:17: UnittestTests.test_a_in_manager
[gw0] FAILED test_unittest_tests.py:14: UnittestTests.test_1_in_manager
...

现在 test_1_in_manager 失败了,因为在 pytest 提供的 manager 字典中没有 1 这个键。

撰写回答