快速入门

创建第一个测试

创建一个名为 test_sample.py 的新文件,其中包含一个函数和一个测试:

Python
# content of test_sample.py
def func(x):
    return x + 1


def test_answer():
    assert func(3) == 5

测试输出:

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

test_sample.py F                                                     [100%]

================================= FAILURES =================================
_______________________________ test_answer ________________________________

    def test_answer():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

test_sample.py:6: AssertionError
========================= short test summary info ==========================
FAILED test_sample.py::test_answer - assert 4 == 5
============================ 1 failed in 0.12s =============================

[100%] 指的是运行所有测试用例的总体进度。在所有测试用例执行完毕后,pytest会显示一个失败报告,因为 func(3) 没有返回 5

您可以使用 assert 语句来验证测试的期望结果。pytest的高级断言内省功能会智能地报告 assert 表达式的中间值,以便您避免使用JUnit遗留方法的许多名称。

运行多个测试案例

pytest 将运行当前目录及其子目录中所有形如 [test]().py*_test.py 的文件。它遵循标准的测试发现规则。

断言抛出了指定异常

使用raise可以在相应代码的抛出的指定异常:

Python
# content of test_sysexit.py
import pytest


def f():
    raise SystemExit(1)


def test_mytest():
    with pytest.raises(SystemExit):
        f()

使用“静默”模式,执行这个测试用例如:

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

在这个例子和后续例子中,-q/--quiet 标志可使输出保持简洁。

使用类组织多条测试用例

一旦你需要开发多条测试用例,你可能会想要使用类来组织它们。使用Pytest可以很轻松的创建包含多条用例的测试类:

Python
# content of test_class.py
class TestClass:
    def test_one(self):
        x = "this"
        assert "h" in x

    def test_two(self):
        x = "hello"
        assert hasattr(x, "check")

pytest 遵循其Python测试发现规则来查找所有测试,因此它会找到以 test_ 为前缀的函数。不需要继承任何东西,但确保将类名以 Test 为前缀,否则该类将被跳过。我们只需通过传递模块的文件名来运行模块:

$ pytest -q test_class.py
.F                                                                   [100%]
================================= FAILURES =================================
____________________________ TestClass.test_two ____________________________

self = <test_class.TestClass object at 0xdeadbeef0001>

    def test_two(self):
        x = "hello"
>       assert hasattr(x, "check")
E       AssertionError: assert False
E        +  where False = hasattr('hello', 'check')

test_class.py:8: AssertionError
========================= short test summary info ==========================
FAILED test_class.py::TestClass::test_two - AssertionError: assert False
1 failed, 1 passed in 0.12s

第一条用例执行成功,第二天用例执行失败。你可以很容易地通过断言中变量的中间值来理解失败的原因。

将测试用例分组在类中可以有以下几个好处:

  • 测试组织
  • 仅在特定类中共享测试用例的测试夹具(fixtures)
  • 在类级别应用标记,并使其隐式应用于所有测试用例

需要注意的是,将测试用例分组在类中时,每个测试用例都有一个独立的类实例。如果每个测试用例共享相同的类实例,将会严重影响测试隔离性,并促使不良的测试实践。以下是详细说明:

Python
# content of test_class_demo.py
class TestClassDemoInstance:
    value = 0

    def test_one(self):
        self.value = 1
        assert self.value == 1

    def test_two(self):
        assert self.value == 1

$ pytest -k TestClassDemoInstance -q
.F                                                                   [100%]
================================= FAILURES =================================
______________________ TestClassDemoInstance.test_two ______________________

self = <test_class_demo.TestClassDemoInstance object at 0xdeadbeef0002>

    def test_two(self):
>       assert self.value == 1
E       assert 0 == 1
E        +  where 0 = <test_class_demo.TestClassDemoInstance object at 0xdeadbeef0002>.value

test_class_demo.py:9: AssertionError
========================= short test summary info ==========================
FAILED test_class_demo.py::TestClassDemoInstance::test_two - assert 0 == 1
1 failed, 1 passed in 0.12s

请注意,添加在类级别的属性是 类属性,因此它们将在测试用例之间共享。

为功能测试请求一个独立的临时目录

pytest 提供了内置的 fixture 函数参数,用于请求任意资源,比如一个独立的临时目录:

Python
# content of test_tmp_path.py
def test_needsfiles(tmp_path):
    print(tmp_path)
    assert 0

在测试函数的签名中列出 tmp_pathpytest 会查找并调用一个 fixture 工厂来在执行测试函数之前创建资源。在测试运行之前,pytest 会创建一个独立的、每次测试调用都不同的临时目录。

$ pytest -q test_tmp_path.py
F                                                                    [100%]
================================= FAILURES =================================
_____________________________ test_needsfiles ______________________________

tmp_path = PosixPath('PYTEST_TMPDIR/test_needsfiles0')

    def test_needsfiles(tmp_path):
        print(tmp_path)
>       assert 0
E       assert 0

test_tmp_path.py:3: AssertionError
--------------------------- Captured stdout call ---------------------------
PYTEST_TMPDIR/test_needsfiles0
========================= short test summary info ==========================
FAILED test_tmp_path.py::test_needsfiles - assert 0
1 failed in 0.12s

有关临时目录处理的更多信息,请参阅临时目录和文件

使用以下命令查找存在的内置 pytest fixture 类型:

pytest --fixtures   # shows builtin and custom fixtures

请注意,该命令将忽略以 _ 开头的 fixture,除非添加 -v 选项。

继续阅读

  • 使用 "usage" 获取命令行调用示例。
  • 使用 "existingtestsuite" 处理现有测试。
  • 查阅 "mark" 以深入了解 pytest.mark 机制。
  • 学习 "fixtures" 为您的测试提供功能基线。
  • 探索 "plugins" 来管理和编写插件。
  • 参考 "goodpractices" 以了解 virtualenv 和测试布局的最佳实践。

results matching ""

    No results matching ""