pytest 其他测试系统-使用unittest基于pytest的测试
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在安装阶段报告没有错误,而是在调用期间引发错误。
更多建议: