目录[-]

主要流程

 

1.安装pytest:pip install pytest

2.基础用法

 

2.1最简单的示例

# coding=utf-8
import pytest

def add(a,b):
    return a+b

class Test_pp:
    def test_add(self):
        assert add(1,2)==3

    @pytest.mark.parametrize("a,b,expected",[(1,2,3),(10,10.5,20.5),(-10.3,2,500)])
    def test_add2(self,a,b,expected):
        assert add(a,b)==expected

返回结果:单元测试执行了3个用例,第1个是测试函数,其余2个是测试类

2.2 断言

def test_assertions():
    # 相等
    assert "hello" == "hello"
    # 包含
    assert "ello" in "hello"
    # 异常
    with pytest.raises(ZeroDivisionError):
        1 / 0
    # 类型检查
    assert isinstance(42, int)
    # 近似相等
    assert 0.1 + 0.2 == pytest.approx(0.3)

3.主要流程

3.1 测试发现

pytest 会自动发现:

  • 文件名以 test_ 开头的文件
  •  _test.py 结尾的文件
  •  Test 开头的类中的 test_ 开头的方法
  •  test_ 开头的函数

3.2 运行测试

# 运行所有测试    
    pytest
# 运行特定文件
    pytest test_sample.py
# 运行特定类
    pytest test_sample.py::TestMathOperations
# 运行特定测试
    pytest test_sample.py::test_addition
# 显示详细输出
    pytest -v
# 显示打印输出
    pytest -s
# 在第一次失败后停止
    pytest -x
# 运行上次失败的测试
    pytest --lf
# 运行带有特定标记的测试
    pytest -m marker_name

4.夹具(Fixtures)

pytest的fixture比unittest的setUp/tearDown更灵活

4.1 基本fixture

scope,使用function和class时,会调用2次

import pytest

@pytest.fixture(scope="function")  # 默认,每个测试函数执行一次
# @pytest.fixture(scope="class")     # 每个测试类执行一次
# @pytest.fixture(scope="module")    # 每个模块执行一次
# @pytest.fixture(scope="session")   # 整个测试会话执行一次
def sample_data():
    data=[1,2,3,4,5]
    print('sample_data--》提供数据')
    yield data # 提供测数据给测试

def test_data_length(sample_data):
    assert len(sample_data)==5

5.参数化测试

装饰器里也写明了三个参数,对应后面列表里的元组数据

@pytest.mark.parametrize("a,b,expected",[(1,2,3),(-1,1,0)])
def test_addition(a,b,expected):
    assert a+b==expected

6.标记(Markers)

@pytest.mark.skip
def test_add_000():
    assert False

@pytest.mark.skipif(reason="功能未实现")
def test_add_001():
    assert False

@pytest.mark.xfail  # 预期失败
def test_add_002():
    assert 3 + 2 == 4
main.py::test_add_000 SKIPPED (unconditional skip)                                                                                                          [ 60%] 
main.py::test_add_001 SKIPPED (功能未实现)                                                                                                                  [ 80%] 
main.py::test_add_002 XFAIL      

返回值分别对三个函数方法做了解释,用于跳过测试

7.插件系统

# 安装常用插件 pip install pytest-cov 
# 测试覆盖率 pip install pytest-xdist 
# 并行测试 pip install pytest-mock 
# 更好的 mock 支持

8.项目结构

project/

----my_package

----__init__.py

----module.py

----tests

----__init__.py

----test_module.py

__init__.py

执行并生成测试用例

pytest_project/

├── my_package/ # 被测代码文件夹

│ └── module.py # 包含需要测试的函数

├── tests/ # 测试代码文件夹

│ └── test_module.py # 测试用例

└── main.py # 主运行文件

import pytest

if __name__=="__main__":
    pytest.main()

问题:这么写,它怎么知道测试代码在哪里?

答案:新建一个pytest.ini文件,在配置文件中,填写如下,获取指定文件夹下的所有py文件,这样会生成一条固定报告文件

[pytest]
python_files = pytest_project/tests/test_*.py    # 默认匹配当前目录的文件
addopts = --html=report.html -v

问题2:如果需要修改报告名称要怎么做?

答案2:新建conftest.py文件,通过钩子函数,动态调整报告名称

import pytest
from datetime import datetime
import os

def pytest_configure(config):
    # 动态生成报告路径(如带时间戳)
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    report_dir = "test_reports"
    report_name = f"report_{timestamp}.html"
    os.makedirs(report_dir, exist_ok=True)
    config.option.htmlpath = os.path.join(report_dir, report_name)