先用一个简单的例子,展示如何使用 pytest 来测试 Python 代码。

首先,确保你已经安装了 pytest。如果没有安装,可以通过 pip 安装:

pip install pytest
示例代码

假设我们有一个简单的 Python 模块 math_functions.py,它包含了一个函数 add_numbers,该函数接收两个数字作为参数并返回它们的和。

# math_functions.py  
  
def add_numbers(a, b):  
    return a + b
编写测试

接下来,我们编写一个测试文件来测试 math_functions.py 中的 add_numbers 函数。测试文件通常以 test_ 开头,以便 pytest 能够自动识别并执行它们。

# test_math_functions.py  
  
from math_functions import add_numbers  
  
def test_add_numbers():  
    assert add_numbers(1, 2) == 3  
    assert add_numbers(-1, 1) == 0  
    assert add_numbers(0, 0) == 0
运行测试

现在,你可以通过命令行运行 pytest 来执行测试。只需在包含测试文件的目录中打开命令行或终端,并输入以下命令:

pytest

会自动找到所有以 test_ 开头的文件,并执行其中的测试函数。对于每个测试函数,pytest 都会创建一个新的测试环境(也称为测试用例),并执行该函数。如果断言(assert 语句)失败,pytest 会报告失败并显示相关的堆栈跟踪。

测试结果

如果 math_functions.py 中的 add_numbers 函数按预期工作,你将看到类似以下的测试结果:

============================= test session starts ==============================  
platform darwin -- Python 3.x.x, pytest-x.x.x, py-1.x.x, pluggy-x.x.x  
rootdir: /path/to/your/project  
collected 1 item  
  
test_math_functions.py .                                                  [100%]  
  
============================== 1 passed in 0.02s ===============================


这里的 . 表示测试通过,而 1 passed 表示总共有 1 个测试通过。

这就是使用 pytest 编写和执行 Python 测试的基本流程。pytest 提供了许多高级功能,包括参数化测试、测试固件(setup/teardown)、跳过和标记测试等,这些功能可以帮助你编写更强大、更灵活的测试。

测复杂点

pytest.mark提供了多种标记(marks)来增强测试用例的灵活性和可管理性。我们将通过几个示例来展示如何使用pytest.mark.parametrize(参数化测试)、pytest.mark.skipif(条件跳过测试)和pytest.mark.xfail(预期失败测试)。

示例测试文件:test_marks.py

import pytest  
  
# 使用 pytest.mark.parametrize 进行参数化测试  
@pytest.mark.parametrize("test_input,expected", [  
    (1, 2),  
    (2, 3),  
    (3, 4),  
])  
def test_increment(test_input, expected):  
    assert test_input + 1 == expected  
  
# 使用 pytest.mark.skipif 条件跳过测试  
import sys  
  
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python 3.7 or higher")  
def test_python_version_dependent_feature():  
    # 假设这是一个依赖于Python 3.7+特性的测试  
    assert True  
  
# 使用 pytest.mark.xfail 标记预期失败的测试  
@pytest.mark.xfail(reason="known issue, will be fixed in the next release")  
def test_known_failure():  
    # 假设这个测试因为某个已知问题而失败  
    assert False  
  
# 还可以将多个标记组合使用  
@pytest.mark.parametrize("num", [1, 2, 3])  
@pytest.mark.skipif(sys.version_info < (3, 8), reason="skip on Python < 3.8")  
def test_multiple_marks(num):  
    # 假设这个测试既需要参数化,又需要在Python 3.8+上运行  
    assert num + 1 > 1, "this should always pass, but might be skipped"  
  
# 标记一个测试类  
@pytest.mark.slow  
class TestSlowFeatures:  
    def test_feature_a(self):  
        # 假设这是一个运行较慢的测试  
        assert True  
  
    def test_feature_b(self):  
        # 另一个运行较慢的测试  
        assert True  
  
# 注意:上面的@pytest.mark.slow本身不会改变测试的执行方式,  
# 它只是给测试或测试类添加了一个标记,你可以通过pytest的命令行选项来过滤或选择这些标记的测试。

 运行测试

要运行这些测试,你只需在命令行中执行pytest命令,并确保你的工作目录包含test_marks.py文件。

pytest
过滤标记的测试

pytest提供了命令行选项来过滤或选择带有特定标记的测试。例如,要只运行标记为slow的测试,你可以使用-m选项:

pytest -m slow
这将会运行所有标记为slow的测试,包括TestSlowFeatures类中的测试。

预期失败和跳过

  • test_known_failure由于被标记为xfail,即使它失败了,pytest也会将其视为通过(即“预期失败”)。
  • test_python_version_dependent_feature会根据你的Python版本决定是否跳过。

通过这种方式,pytest.mark为测试提供了更多的灵活性和控制力。

另一个测试框 架参见:Python 测试框架:unittest (unittest.mock)

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐