pytest 其他测试系统-使用unittest基于pytest的测试

2022-03-29 17:26 更新

pytest 支持开箱即用地运行基于 Python 单元测试的测试。 它旨在利用现有的基于单元测试的测试套件将 pytest 用作测试运行器,并允许逐步调整测试套件以充分利用 pytest 的功能。

要使用 pytest 运行现有的 ​unittest样式的测试套件,请键入:

pytest tests

pytest 将自动在 ​test_*.py​ 或 ​*_test.py​ 文件中收集 ​unittest.TestCase​ 子类及其测试方法。

几乎所有单元测试功能都受支持:

  • @unittest.skip style decorators
  • setUp/tearDown
  • setUpClass/tearDownClass
  • setUpModule/tearDownModule

到目前为止,pytest 不支持以下功能:

  • load_tests protocol
  • subtests

开箱即用的好处

通过使用 pytest 运行您的测试套件,您可以利用多个功能,在大多数情况下无需修改现有代码:

  • 获得更多信息的追溯
  • 标准输出和标准错误捕获
  • 使用 ​-k​ 和 ​-m​ 标志测试选择选项
  • 在第一次(或 ​N​ 次)失败后停止
  • -pdb​ 用于调试测试失败的命令行选项
  • 使用 ​pytest-xdist​ 插件将测试分发到多个 CPU
  • 使用简单的断言语句而不是 ​self.assert*​ 函数(​unittest2pytest在这方面非常有帮助)

unittest.TestCase 子类中的 pytest 功能

以下 pytest 功能在 ​unittest.TestCase子类中工作:

  • Marks​: ​skip​, ​skipif​, ​xfail
  • Auto-use fixtures

以下 pytest 功能不起作用,并且由于设计理念不同,可能永远不会起作用:

  • Fixtures
  • Parametrization
  • ​Custom ​hooks

第三方插件可能运行良好,也可能运行不佳,具体取决于插件和测试套件。

使用标记将 pytest fixture混合到 unittest.TestCase 子类中

在pytest中运行unittest允许你在unittest中使用它的​fixture​机制。TestCase风格测试。假设你已经浏览了pytest ​fixture​的特性,让我们开始一个集成了​pytest db_class fixture​的例子,设置一个类缓存的数据库对象,然后从一个unittest风格的测试引用它:

# content of conftest.py

# we define a fixture function below and it will be "used" by
# referencing its name from tests

import pytest


@pytest.fixture(scope="class")
def db_class(request):
    class DummyDB:
        pass

    # set a class attribute on the invoking test context
    request.cls.db = DummyDB()

这定义了一个​fixture​函数​db_class​,如果使用,则对每个测试类调用一次,并将类级别的​db​属性设置为一个​DummyDB​实例。​fixture​函数通过接收一个特殊的请求对象来实现这一点,该请求对象提供对请求测试上下文的访问,比如cls属性,它表示使用​fixture​的类。这种体系结构将​fixture​的编写与实际的测试代码分离开来,并允许通过最小引用(​fixture​名称)重用​fixture​。我们来写一个​unittest​。​TestCase​类使用我们的​fixture​定义:

# content of test_unittest_db.py

import unittest
import pytest


@pytest.mark.usefixtures("db_class")
class MyTest(unittest.TestCase):
    def test_method1(self):
        assert hasattr(self, "db")
        assert 0, self.db  # fail for demo purposes

    def test_method2(self):
        assert 0, self.db  # fail for demo purposes

@pytest.mark.usefixtures("db_class")类装饰器确保每个类调用一次 pytest ​fixture​函数 ​db_class​,由于故意失败的断言语句,我们可以查看回溯中的self.db值:

$ pytest test_unittest_db.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 2 items

test_unittest_db.py FF                                               [100%]

================================= FAILURES =================================
___________________________ MyTest.test_method1 ____________________________

self = <test_unittest_db.MyTest testMethod=test_method1>

    def test_method1(self):
        assert hasattr(self, "db")
>       assert 0, self.db  # fail for demo purposes
E       AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef0001>
E       assert 0

test_unittest_db.py:10: AssertionError
___________________________ MyTest.test_method2 ____________________________

self = <test_unittest_db.MyTest testMethod=test_method2>

    def test_method2(self):
>       assert 0, self.db  # fail for demo purposes
E       AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef0001>
E       assert 0

test_unittest_db.py:13: AssertionError
========================= short test summary info ==========================
FAILED test_unittest_db.py::MyTest::test_method1 - AssertionError: <conft...
FAILED test_unittest_db.py::MyTest::test_method2 - AssertionError: <conft...
============================ 2 failed in 0.12s =============================

这个默认的 pytest 回溯显示这两个测试方法共享同一个 ​self.db​ 实例,这是我们在上面编写类范围的​fixture函数时的意图。

使用 autouse fixture和访问其他fixture

虽然对于给定的测试,显式声明所需使用的​fixture​通常更好,但有时您可能希望在给定的上下文中自动使用​fixture​。毕竟,​unittest-setup​的传统风格要求使用这种隐式的​fixture​编写,您可能已经习惯或喜欢它了。

您可以使用​@pytest.fixture(autuse =True)​标记​fixture​函数,并在希望使用它的上下文中定义​fixture​函数。让我们看一下​initdir fixture​,它使​TestCase​类的所有测试方法在一个带有预先初始化的​samplefile.ini​的临时目录中执行。我们的​initdir fixture​本身使用pytest内置的​tmp_path fixture​来委托创建每个测试的临时目录:

# content of test_unittest_cleandir.py
import os
import pytest
import unittest


class MyTest(unittest.TestCase):
    @pytest.fixture(autouse=True)
    def initdir(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)  # change to pytest-provided temporary directory
        tmp_path.joinpath("samplefile.ini").write_text("# testdata")

    def test_method(self):
        with open("samplefile.ini") as f:
            s = f.read()
        assert "testdata" in s

由于有​autuse​标志,​initdir fixture​函数将用于定义它的类的所有方法。这是在类上使用​@pytest.mark.usefixture ("initdir")​标记的快捷方式,就像前面的例子一样。

运行这个测试模块:

$ pytest -q test_unittest_cleandir.py
.                                                                    [100%]
1 passed in 0.12s

上述测试通过,因为​initdir fixture​函数在​test_method​之前被执行。

unittest.TestCase​ 方法不能直接接收​fixture​参数作为实现,这可能会影响运行通用 ​unittest.TestCase​ 测试套件的能力。

上面的 ​usefixtures ​和 ​autouse ​示例应该有助于将 pytest ​fixture​混合到单元测试套件中。

您还可以逐渐从 ​unittest.TestCase​ 的子类化转移到普通断言,然后开始逐步受益于完整的 pytest 功能集。

由于两个框架之间的架构差异,基于单元测试的设置和拆卸是在测试的调用阶段执行的,而不是在pytest的标准设置和拆卸阶段。在某些情况下,理解这一点很重要,特别是在推理错误时。例如,如果基于单元测试的套件在安装期间出现错误,pytest在安装阶段报告没有错误,而是在调用期间引发错误。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号