一、什么是 pytest

Pytest 是一个基于 Python 的开源测试框架,对于接口自动化测试来说,可以自动识别到测试用例,不需要再动手编写 main 函数并调用测试用例

安装命令:

pip install pytest

安装成功按运⾏规则使用会出现 绿色的三角形 标志
在这里插入图片描述

二、为什么测试需要 pytest

不知道你是否和我一样有疑惑,刚开始学习 Python 自动化测试时,我有一个疑问:

Python 文件直接运行就能得到结果,为什么还需要学习 Pytest?

例如下面这段代码:

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

print(add(1, 2))

直接运行:

3

程序正常执行,结果也正确。
既然 Python 已经能够运行代码,那为什么自动化测试项目几乎都在使用 Pytest 呢?

直接运行 Python 代码存在哪些问题

实际上,当项目规模变大后,仅仅依靠 print() 或手动运行代码已经无法满足测试需求。

问题1:无法自动判断结果是否正确

例如:

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

print(add(1, 2))

控制台输出:

3

程序确实运行成功了。
但是:

  • 谁来判断结果是否正确?
  • 是人工查看吗?
  • 如果有1000个测试场景怎么办?

问题2:无法批量执行测试

假设有100个功能:

  • 登录
  • 注册
  • 修改密码
  • 上传头像
  • 搜索商品
  • 提交订单 …

如果每个功能都单独运行:

python login.py
python register.py
python order.py

效率非常低。

而测试框架可以一次执行全部测试:

pytest

自动发现并运行所有测试用例。

问题3:无法生成测试报告

当项目拥有几百条测试用例时,开发和测试最关心的是:

  • 通过多少条?
  • 失败多少条?
  • 哪些功能失败了?
  • 错误原因是什么?

如果只依靠 Python 运行:

print("执行成功")

很难统计测试结果。

而 Pytest 可以自动输出:

def test_03():
    json = {
        "name":"hr",
        "age":110
    }
    json_schema = {
        "type": "object",
        "properties": {
            "name": {
                "type": "string",
            },
            "age": {
                "type": "integer",
                "exclusiveMinimum":0,
                "exclusiveMaximum":100
            }
        },
        "required": [
            "name",
            "age"
        ]
    }
    validate(instance=json,schema=json_schema)
def test_04():
    json = {
        "name":"hrrrrr",
        "age":10
    }
    json_schema = {
        "type": "object",
        "properties": {
            "name": {
                "type": "string",
                "pattern":"\S{2,5}"
            },
            "age": {
                "type": "integer",
            }
        },
        "required": [
            "name",
            "age"
        ]
    }
    validate(instance=json,schema=json_schema)

控制台输出:

在这里插入图片描述
具体到哪个位置哪个部分出现了哪些问题,甚至生成 HTML、Allure 测试报告。

问题4:无法管理测试数据

例如登录测试:

admin / 123456
test / 888888
guest / 666666

如果使用普通 Python:

test_login_admin()
test_login_test()
test_login_guest()

会出现大量重复代码。

Pytest 支持参数化:

@pytest.mark.parametrize(
    "username,password",
    [
        ("admin","123456"),
        ("test","888888"),
        ("guest","666666")
    ]
)

一套代码即可执行多组数据。

问题5:无法统一管理测试环境

在实际项目中,经常需要:

  • 登录系统
  • 创建测试数据
  • 连接数据库
  • 初始化浏览器
  • 清理测试环境

如果每个测试都重复编写:

login()
connect_db()
open_browser()

维护成本极高。
Pytest 的 Fixture 机制可以统一管理这些公共操作。

三、Pytest 的基本使用

1. 编写第一个测试用例

判定两数之和是否大于3 :

def add(a, b): 
	return a + b 
def test_add(): 
	assert add(1, 2) == 3

执行测试,在命令行中输入:

pytest

在这里插入图片描述
执行结果(需要添加命令参数):

在这里插入图片描述

2. 命名规则

既然执行测试需要在命令行输入,那 pytest 是怎么知道我们是要测试哪一个呢,其实这就跟⽤例命名规则有关系了,并不是所有的⽅法都可以直接运⾏

Pytest 默认识别以下规则:

  1. 文件名必须以 test_ 开头或者 _test 结尾
  2. 测试类必须以 Test 开头,并且不能有 __init__ 方法。
  3. 测试方法必须以 test开头

方法名

def test02_01():
    print("test02_01")
def test02_02():
    print("test02_02")
def test02_03():
    print("test02_03")

文件名

在这里插入图片描述

# 文件名为 test_02.py
def test02_01():
    print("test02_01")
def test02_02():
    print("test02_02")
def test02_03():
    print("test02_03")

类名

class Test02():
    def test02_01(self):
        print("test02_01")

    def test02_02(self):
        print("test02_02")

    def test02_03(self):
        print("-test02_03")

注意!!:Python类中不可以添加 init⽅法

在这里插入图片描述
由于 pytest 的测试收集机制,测试类中不可以定义_ _init_ _方法。pytest 采用自动发现机制来收集测试用例。它会自动实例化测试类并调用其所有以test结尾的方法作为测试用例。如果测试类中定义了__init__方法,那么当pytest实例化该类时,__init__方法会被调用,这可能会掩盖测试类的实际测试逻辑,并引入额外的副作用,影响测试结果的准确性。

为了避免使用_init__方法,建议在pytest中使用其他替代方案,如使用setUp()tearDown()方法、使用类属性、使用fixture函数

3. pytest命令参数

在实际项目中,我们很少直接使用:pytest

因为不能直接看到接口请求日志、入参出参等信息

举个例子:

def add(a, b):
	return a + b
def test_add():
	print(add(1, 2))

直接使用:

pytest

执行结果:
在这里插入图片描述
就只能看到用例是否执行成功

如果使用参数 -sv:

pytest -sv

执行结果:
在这里插入图片描述
既能输出具体的测试用例名称,还能输出print语句

pytest 常见命令行参数

我们可以结合不同参数完成指定场景的测试执行,以下是一些常用的pytest 命令行参数及其使用说明

命令 描述 备注
pytest 自动搜索当前目录及子目录下的测试用例并执行 最常用
pytest -v 显示更详细的测试执行信息 verbose(详细模式)
pytest -s 显示测试中的 print() 输出 调试时常用
pytest -vs 同时显示详细信息和打印输出 实际工作中最常用
pytest test_module.py 执行指定测试文件 指定模块运行
pytest test_dir/ 执行指定目录下的所有测试 批量执行
pytest test_module.py::test_func 执行指定测试函数 精准定位测试
pytest test_module.py::TestClass 执行指定测试类 常用于调试
pytest -k <keyword> 执行名称包含指定关键字的测试 模糊匹配
pytest -m <marker> 执行指定标记的测试 需提前定义 Marker
pytest -q 简化输出信息 quiet(静默模式)
pytest -x 遇到第一个失败立即停止 冒烟测试常用
pytest --lf 仅执行上次失败的测试用例 Last Failed
pytest --ff 优先执行上次失败的测试用例 Failed First
pytest --pdb 测试失败后进入调试模式 类似断点调试
pytest --maxfail=2 最多失败 2 次后停止执行 控制失败次数
pytest --html=report.html 生成 HTML 测试报告 需安装 pytest-html
pytest --cov 统计测试覆盖率 需安装 pytest-cov
pytest --cov=app 统计指定目录覆盖率 推荐使用
pytest -n 4 使用 4 个进程并发执行 需安装 pytest-xdist
pytest --reruns 3 失败后自动重试 3 次 需安装 pytest-rerunfailures
pytest --collect-only 仅收集测试用例,不执行 查看测试发现结果

4. pytest配置⽂件(pytest.ini)

随着项目规模增大,如果每次执行测试都需要输入大量命令参数
例如:

pytest -vs -m smoke --html=report.html

又臭又长,不仅麻烦,而且团队成员之间执行方式可能不一致。

因此 Pytest 提供了配置文件机制,可以将常用配置统一管理

我们需要创建 pytest.ini 文件,通常把它放在项目根目录

pytest.ini 常用配置项

配置项 作用说明
addopts 指定默认执行参数,等同于每次运行 pytest 时自动追加这些命令行参数
testpaths 指定测试用例搜索目录
python_files 指定测试文件匹配规则
python_classes 指定测试类匹配规则
python_functions 指定测试函数或测试方法匹配规则
norecursedirs 指定搜索测试时需要忽略的目录
markers 注册测试标记(Marker),用于分类执行测试用例

示例:详细输出 testcase 包下文件名以 test_ 开头且方法以 Test 开头 的所有用例

project
│
├── testcase
│   └── test_login.py
│
└── pytest.ini
[pytest]
addopts = -vs
; 指定搜索测试的目录
testpaths = ./testcase
; 指定文件名,即使他不是在规则命名
python_files = test_*.py
; 指定类名,即使他不是在规则命名
python_classes = Test*
; 指定方法名,即使他不是在规则命名
python_functions = test_*
class TestLogin:
    def test_login_success(self):
        print("登录成功")

    def test_login_fail(self):
        print("登录失败")

配置好 pytest.ini ⽂件后,命令⾏执⾏ pytest 命令即可,⽆需再额外指定其他参数:
在这里插入图片描述

5. 前后置

在命名规则的章节,我挖了一个坑,既然不能测试类__init__方法,那该如何进⾏数据的初始化?

在测试框架中,前后置 是指在执⾏测试⽤例前和测试⽤例后执⾏⼀些额外的操作,这些操作可以⽤于设置测试环境、准备测试数据等,以确保测试的可靠性
pytest 框架提供三种方法做前后置的操作:

  • setup_methodteardown_method:这两个方法用于类中的每个测试方法的前置和后置操作。
  • setup_classteardown_class:这两个方法用于整个测试类的前置和后置操作。
  • fixture:这是 pytest 推荐的方式来实现测试用例的前置和后置操作。fixture 提供了更灵活的控制和更强大的功能。

setup_method 和 teardown_method

作用:

  • 每个测试方法执行前运行一次 setup_method
  • 每个测试方法执行后运行一次 teardown_method
    示例:
class TestLogin:

    def setup_method(self):
        print("登录系统")

    def teardown_method(self):
        print("退出系统")

    def test_order(self):
        print("提交订单")

    def test_pay(self):
        print("订单支付")

执行结果:

在这里插入图片描述

setup_method
↓
测试方法
↓
teardown_method

setup_class 和 teardown_class

作用:

  • 整个测试类执行前运行一次 setup_class
  • 整个测试类执行后运行一次 teardown_class
    示例:
class TestLogin:

    def setup_class(self):
        print("启动浏览器")

    def teardown_class(self):
        print("关闭浏览器")

    def test_order(self):
        print("提交订单")

    def test_pay(self):
        print("订单支付")

执行结果:
在这里插入图片描述

setup_class
↓
所有测试方法
↓
teardown_class

6. 断言机制(Assert)

Pytest 使用 Python 原生 assert 进行断言,用于验证实际结果与预期结果是否一致

基本语法:

assert 条件, 错误信息

条件:必须是⼀个布尔表达式
错误信息:当条件为假时显⽰的错误信息,可选

示例:

import pytest
import requests

# ====================== 基础数据类型断言示例 ======================
def test_base():
    """整数、字符串断言"""
    # 整数断言
    a = 1
    b = 1
    assert a == b
    # 字符串断言(此处两个字符串不一致,运行会失败)
    str1 = "hello"
    str2 = "hello world"
    assert str1 == str2

def test_ds():
    """列表、元组、字典容器类型断言"""
    # 列表断言
    expect_list = [1, 'hello', 3.14]
    actual_list = [1, 'hello', 3.14]
    assert expect_list == actual_list

    # 元组断言
    expect_tuple = (1, 'hello', 3.14)
    actual_tuple = (1, 'hello', 3.14)
    assert expect_tuple == actual_tuple

    # 字典断言
    expect_dict = {'name': 'hr', 'age': 25}
    actual_dict = {'name': 'hr', 'age': 25}
    assert expect_dict == actual_dict

def divide(a, b):
    """除法工具函数:除数非0断言"""
    assert b != 0, "除数不能为0"
    return a / b

def test_divide():
    """函数返回值断言、异常场景断言"""
    # 正常除法
    print(divide(10, 2))
    # 除数为0触发断言报错
    print(divide(10, 0))

# ====================== 接口自动化断言示例 ======================
def test_api01():
    """全量校验接口返回JSON完整数据"""
    url = "https://jsonplaceholder.typicode.com/posts/1"
    r = requests.get(url=url)
    actual_data = r.json()
    expect_data = {
        "userId": 1,
        "id": 1,
        "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
        "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
    }
    assert expect_data == actual_data

def test_api02():
    """只校验JSON返回结果中的单个关键字段"""
    url = "https://jsonplaceholder.typicode.com/posts/1/comments"
    r = requests.get(url=url)
    # 校验返回列表第一条数据的id字段
    assert r.json()[0]['id'] == 1

def test_api03():
    """返回非标准JSON,通过文本包含做断言"""
    url = "https://jsonplaceholder.typicode.com/"
    r = requests.get(url=url)
    target_text = "Use your own data"
    assert target_text in r.text

接口自动化断言示例里用到是 免费学习API资源:http://jsonplaceholder.typicode.com/

7. 参数化

当同一个测试逻辑,需要使用多组测试数据验证,就需要用到参数化

假设要测试登录功能

测试数据:

用户名 密码 预期结果
admin 123456 登录成功
test 888888 登录成功
admin 123 登录失败

如果不使用参数化:

def test_login_01():
    username = "admin"
    password = "123456"

    assert login(username, password) == "登录成功"


def test_login_02():
    username = "test"
    password = "888888"

    assert login(username, password) == "登录成功"


def test_login_03():
    username = "admin"
    password = "123"

    assert login(username, password) == "登录失败"

代码重复,不方便维护,数据量大时代码爆炸

使用参数化:

Pytest 提供:
@pytest.mark.parametrize()装饰器 实现数据驱动测试

示例:

import pytest

@pytest.mark.parametrize(
    "username,password",
    [
        ("admin", "123456"),
        ("test", "888888"),
        ("guest", "666666")
    ]
)
def test_login(username, password):
    print(username, password)

单参数传递

对单个参数得参数化,参数为统一的类型
示例:

  • "data"形参名,测试函数 test01(data) 的入参变量;
  • (1,2,3)参数数据源(元组),一共 3 组数据:1、2、3。
@pytest.mark.parametrize("data",(1, 2, 3))
def test01(data):
    print(data)

执行结果:
在这里插入图片描述
参数不是统一的类型
示例:

@pytest.mark.parametrize("data",(1, 'hr', True))
def test02(data):
    print(data)

执行结果:
在这里插入图片描述

多参数传递

对多个参数得参数化
示例:

  • "test_input,expected":两个入参变量名,和函数形参一一对应;
  • [("3+5",8),("2+4",6),("6*9",54)]:列表嵌套元组,3 组测试用例数据
@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+4",6),("6*9",54)])
def test_eval(test_input, expected):
    assert eval(test_input) == expected

执行结果:
在这里插入图片描述

对类进行参数化

也可以在类或模块上使⽤ parametrize 标记,这将使⽤参数集调⽤多个函数
示例:

@pytest.mark.parametrize("n,expected",[(1, 2),(2, 3)])
class Test01:
    def test_simple_case(self, n, expected):
        assert n + 1 == expected
    def test_weird_simple_case(self, n, expected):
        assert n + 1 == expected

执行结果:
在这里插入图片描述
对模块中的所有测试进行参数化,可以使用 pytestmark 全局变量赋值

pytestmark = pytest.mark.parametrize("n,expected",[(1, 2),(2, 3)])

class Test01:
    def test_simple_case(self, n, expected):
        assert n + 1 == expected
    def test_weird_simple_case(self, n, expected):
        assert n + 1 == expected
class Test02:
    def test_simple_case02(self, n, expected):
        assert n + 1 == expected
    def test_weird_simple_case02(self, n, expected):
        assert n + 1 == expected

执行结果:
在这里插入图片描述

自定义参数化数据源

示例:

  • data_provider() 是自定义数据函数,返回列表 [“a”,“b”,“c”]
  • parametrize 第二个参数直接调用函数,拿到数据源:“a”、“b”、"c"三组数据
def data_provider():
    return ["a","b","c"]
@pytest.mark.parametrize("data",data_provider())
def test_data(data):
    assert data != None
    print(data)

执行结果:

在这里插入图片描述

8. Fixture 机制

pytest 中的 fixture是⼀种强⼤的机制,⽤于提供测试函数所需的资源或上下⽂。它可以⽤于设置测试环境、准备数据等。

简单来说有四个方面:

  1. 测试前置处理
  2. 测试后置处理
  3. 数据共享
  4. 环境初始化

使⽤与不使⽤fixture标记对比

不使用:
未标记 fixture 方法的调用–函数名来调用

def fixrure_01():
    print("第一个fixture方法")
def test_01():
    fixrure_01()
    print("第一个测试用例")

执行结果:
在这里插入图片描述
使用:

@pytest.fixture
def fixrure_01():
    print("第一个fixture方法")


def test_01(fixrure_01):
    print("第一个测试用例")

执行结果:
在这里插入图片描述
未标记 fixture方法 的调用与 fixture 标记的方法调用完全不一样,前者需要在方法体中调用,而后者可以将函数名作为参数进行调用

测试脚本中存在的很多重复的代码、公共的数据对象时,使用 fixture 最为合适

示例1:重复的代码

@pytest.fixture
def fixrure_01():
    print("第一个fixture方法")
def test_01(fixrure_01):
    print("第一个测试用例")
def test_02(fixrure_01):
    print("第二个测试用例")
def test_03(fixrure_01):
    print("第三个测试用例")

执行结果:
在这里插入图片描述
示例2:测试博客

@pytest.fixture
def login():
    print("登录")
def test_blogList(login):
    print("测试博客列表页")

执行结果:
在这里插入图片描述

fixture 可以嵌套使用

示例:

@pytest.fixture
def first():
    print("first")
@pytest.fixture
def second(first):
    print("second")
def test(second):
    print("test")

执行结果:

在这里插入图片描述

Yield Fixture

Yield Fixture 是 pytest 测试框架中,用 yield 实现前置准备 + 后置清理一体化的 fixture 写法

示例1:

执行流程:

  • pytest 运行 test_05,发现参数 operator,调用对应 fixture
  • 执行到 yield 100,暂停,把 100 传给测试函数
  • 测试函数执行完毕(无论成功 / 抛异常)
  • pytest 回到 fixture,执行 yield 之后的 print
@pytest.fixture
def operator():
    print("前置操作:数值初始化")

    yield 100

    print("后置操作:数据清理")
def test_05(operator):
    assert operator == 100

执行结果:
在这里插入图片描述

示例2:利用yield 操作文件

txt文件内容:

在这里插入图片描述

def file_read():
    print("打开文件")
    f = open('text.txt', 'r')
    yield f
    print("关闭文件")
    f.close()
def test_file(file_read):
    r = file_read
    str = r.read(-1)
    print(str)

执行结果:
在这里插入图片描述

带参数的fixture

带参数的 Fixture = 可以动态产生不同测试环境或资源的 Fixture

语法格式:

pytest.fixture(scope='', params='', autouse='', ids='', name='')

查看 fixture 源码可知,参数覆盖很广
在这里插入图片描述
参数详解:

参数 说明 示例
scope 控制 Fixture 的作用范围(生命周期) scope="function"
autouse 是否自动执行 Fixture,默认 False autouse=True
params Fixture 参数化,每个参数都会执行一次 Fixture params=["Chrome","Edge"]
ids params 配合使用,为参数设置可读名称 ids=["谷歌","微软"]
name 给 Fixture 指定别名 name="login_user"
scope

scope 参数详解:

取值 作用
function(默认) 每个测试函数执行一次 Fixture
class 同一个测试类共享一个 Fixture
module 同一个模块(一个 .py 文件)共享一个 Fixture
session 整个测试会话(所有被 pytest 运行的用例)共享一个 Fixture
示例1:scope=“function”
# 1. function
@pytest.fixture(scope="function")
def fixture_01():
    print("初始化")
    yield
    print("清理")
class TestCase01:
    def test_01(self,fixture_01):
        print("第一个测试用例")

    def test_02(self,fixture_01):
        print("第二个测试用例")

执行结果:
在这里插入图片描述

示例2:scope=“class”
# 2. class
@pytest.fixture(scope="class")
def fixture_02():
    print("初始化")
    yield
    print("清理")


class TestCase02:
    def test_01(self, fixture_02):
        print("第一个测试用例")

    def test_02(self, fixture_02):
        print("第二个测试用例")

执行结果:
在这里插入图片描述
scope 默认为 function,这里的 function 可以省略不写,当 scope="function" 时,每个测试函数都会调用一次fixturescope="class" 时,在同一个测试类中,fixture会在类中的第一个测试函数开始前执行一次,并在类中的最后一个测试函数结束后执行清理。

示例3:scope=“module” 和 scope=“session”

modulesession ⽤于实现全局的前后置应⽤,这⾥需要多个⽂件的配合,这种结合的方式使得可以在整个测试项目中定义和维护通用的前后置逻辑,使测试代码更加模块化和可维护

规则:

  • conftest.py 是一个单独存放的夹具配置文件,名称是固定的不能修改
  • 你可以在项目中的不同目录下创建多个 conftest.py 文件,每个 conftest.py 文件都会对其所在目录及其子目录下的测试模块生效
  • 在不同模块的测试中需要用到 conftest.py 的前后置功能时,不需要做任何的 import 导入操作
  • 作用: 可以在不同的 .py 文件中使用同一个 fixture 函数

项⽬结构:

object/
├── conftest.py       # 对所有 object/ 下的用例生效
├── cases/
│   └── test_case05.py
│  └── test_case06.py

当scope=“module” 时:

conftest.py:

@pytest.fixture(scope="module")
def fixture_01():
    print("初始化")
    yield
    print("清理")

test_case05.py:

class TestCase01:
    def test_01(self,fixture_01):
        print("第一个测试用例")

    def test_02(self,fixture_01):
        print("第二个测试用例")
class TestCase02:
    def test_01(self,fixture_01):
        print("第一个测试用例")

    def test_02(self,fixture_01):

        print("第二个测试用例")

test_case06.py

class TestCase03:
    def test_01(self,fixture_01):
        print("第一个测试用例")

    def test_02(self,fixture_01):
        print("第二个测试用例")

执行结果:
在这里插入图片描述

当scope=“session” 时:

conftest.py:

@pytest.fixture(scope="session")
def fixture_01():
    print("初始化")
    yield
    print("清理")

test_case05.py

class TestCase01:
    def test_01(self,fixture_01):
        print("第一个测试用例")

    def test_02(self,fixture_01):
        print("第二个测试用例")
class TestCase02:
    def test_01(self,fixture_01):
        print("第一个测试用例")

    def test_02(self,fixture_01):

        print("第二个测试用例")

test_case06.py

class TestCase03:
    def test_01(self,fixture_01):
        print("第一个测试用例")

    def test_02(self,fixture_01):
        print("第二个测试用例")

执行结果:
在这里插入图片描述
对比:
在这里插入图片描述

autouse

autouse 参数默认为 False 。如果设置为 True则每个测试函数都会⾃动调⽤该 fixture,⽆需显式传⼊

项⽬结构:

object/
├── conftest.py       # 对所有 object/ 下的用例生效
├── cases/
│   └── test_case05.py
│  └── test_case06.py

示例1:autouse=Flase
conftest.py:

@pytest.fixture(scope="session",autouse=Flase)
def fixture_01():
    print("初始化")
    yield
    print("清理")

test_case05.py

class TestCase01:
    def test_01(self,fixture_01):
        print("第一个测试用例")

    def test_02(self,fixture_01):
        print("第二个测试用例")
class TestCase02:
    def test_01(self,fixture_01):
        print("第一个测试用例")

    def test_02(self,fixture_01):

        print("第二个测试用例")

test_case06.py

class TestCase03:
    def test_01(self,fixture_01):
        print("第一个测试用例")

    def test_02(self,fixture_01):
        print("第二个测试用例")

执行结果:
在这里插入图片描述

示例2:autouse=True

conftest.py:

@pytest.fixture(scope="session",autouse=True)
def fixture_01():
    print("初始化")
    yield
    print("清理")

test_case05.py

class TestCase01:
    def test_01(self):
        print("第一个测试用例")

    def test_02(self):
        print("第二个测试用例")

class TestCase02:
    def test_01(self):
        print("第一个测试用例")

    def test_02(self):
        print("第二个测试用例")

test_case06.py

class TestCase03:
    def test_01(self):
        print("第一个测试用例")

    def test_02(self):
        print("第二个测试用例")

执行结果:

在这里插入图片描述

params

主要是用来给 fixture 做参数化让同一个 fixture 能生成多份不同的实例,依赖它的所有测试用例,都会按参数列表自动执行多次

示例1:

  1. pytest 发现 data_provider 这个 fixture,它的 params=[1,2,3] 会生成 3 组参数实例
  2. 依赖它的测试用例 test_data,会自动执行 3 次:
    • 第 1 次:request.param = 1 → data_provider 返回 1 → 用例打印 1
    • 第 2 次:request.param = 2 → data_provider 返回 2 → 用例打印 2
    • 第 3 次:request.param = 3 → data_provider 返回 3 → 用例打印 3
@pytest.fixture(params=[1, 2, 3])
def data_provider(request):
    return request.param
def test_data(data_provider):
    print(data_provider)

执行结果:
在这里插入图片描述

注意!!:实现参数化,前面学习过的 @pytest.mark.parametrize 也可以实现,但通过fixture可以实现参数化,我们怎么选?

  1. 如果你是单条用例需要多组输入 / 输出(比如测试同一个函数的不同参数),用 @pytest.mark.parametrize 更直接
  2. 如果你是多个用例都需要同一套参数(比如都要跑 admin/user/guest 三个角色),用 fixture(params=...) 更高效,避免重复写参数列表。
  3. 如果参数需要带前置 / 后置操作(比如每个用户都要登录 / 登出),优先用 fixture(params=...),可以把登录登出逻辑统一写在 fixture 里。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐