目录

1.介绍

2.安装方式

3.常见第三方插件

4.pytest语法规则

5.pytest常用运行方式

6.pytest命令行选项分类

7.pytest的前后置处理

前置处理(Setup)

后置处理(Teardown)

8.conftest的使用

conftest.py介绍

yield关键字作用

@pytest.fixture()

参数

fixture带参数传递

pytest接口自动化框架的运行顺序

9.pytest标签

1.@pytest.mark.parametrize()

2.skip跳过

@pytest.mark.skip()

@pytest.mark.skipif()

3.xfail标记用例为失败

4.使用自定义标记mark,只执行部分用例

钩子函数

10.pytest的ini文件


1.介绍

pytest 是一个功能强大且易于使用的 Python 测试框架,用于编写单元测试、集成测试

和功能测试。

它提供了丰富的功能和灵活的用法,使得编写和运行测试变得简单而高效。

pytest的优点:

1. 简单易用:pytest的语法非常简洁,我们不需要通过大量的配置和其他方式就能直接启动pytest完成测试 。

2. 自动发现与自动测试 : pytest 能够自动发现项目中的测试文件和测试函数,无需手动编写繁琐的配置。

3. 丰富断言: pytest 内置了丰富的断言库,可以轻松地进行测试结果的判断。4. 支持参数化测试: pytest 支持参数化测试,能够快速地对多组输入进行测试。

5. 插件与第三方库丰富: pytest 有着丰富的插件生态系统,可以通过插件扩展各种功能,比如覆盖率测试、测试报告生成等。

2.安装方式

在cmd/终端,输入:

pip install pytest

pip install pytest == 7.3.1

验证是否安装成功:

pytest --version

一旦在终端 有确认的版本号,就证明安装成功。

如果安装慢 / 报错,用国内镜像(推荐)

pip install pytest -i https://pypi.tuna.tsinghua.edu.cn/simple

PyCharm(图形界面安装)

  • 打开 PyCharm
  • 进入 File → Settings → Project: 你的项目 → Python Interpreter
  • + 号
  • 搜索 pytest
  • 点击 Install Package

3.常见第三方插件

1. pytest-sugar :这个插件为Pytest的测试用例添加了进度条,让测试过程更加可视

化。

2. pytest-rerunfailures :这个插件通过重跑机制来消除不稳定失败的测试用例。如果

某个测试用例失败了,它会尝试重新运行该用例,直到该用例稳定通过或者达到最大

重试次数。

3. pytest-xdist :这是一个分布式执行插件,能够实现多个CPU或者主机执行,动态决

定测试用例的执行顺序。使用这个插件可以加快测试的执行速度,因为它能够同时运

行多个测试用例。

4. pytest-assume :这个插件支持在测试用例中添加假设断言,类似于其他语言的 ass

ume 函数。如果假设断言失败了,测试用例会立即跳过而不报告失败。

5. pytest-html :这个插件可以生成HTML格式的测试报告。它支持多种HTML测试报告

生成方式,并可以与 pytest-xdist 等插件结合使用。

pytest~=7.4.2 : 7.4.x 系列 。自动化测试核心框架,用来写、组织、运行测试用例 。

  • allure-pytest:生成美观、可展示的企业级测试报告, 带步骤、截图、日志,可用于项目复盘 。
  • pytest-rerunfailures: 用例失败自动重跑,解决接口不稳定、偶现失败问题 ,提高稳定性
  • pytest-xdist:多进程并行执行,大幅提升用例执行速度
  • pytest-html:简单 HTML 报告
  • pytest-assume:多重断言,不中断
  • pytest-ordering:控制用例执行顺序

pytest-base-url : 给 pytest 提供统一的 base_url 配置,避免硬编码接口地址

requests~=2.31.0 : 发送 HTTP/HTTPS 请求的核心库,接口自动化必用

pyyaml~=6.0.1 : 读取 / 写入 YAML 配置文件,用来管理接口用例、配置、环境变量 。

jsonpath~=0.82.2 : 解析 JSON 响应,方便提取接口返回的字段(比如 token、订单号)

~=x.y.z 表示:兼容更新到次版本,不跨大版本

4.pytest语法规则

Pytest默认规则:

测试文件:文件名必须test_*.py或者*_test.py

测试类:类名必须是TestXxx。可以使⽤驼峰命名法(CamelCase) 。

测试用例函数:函数名(测试用例)必须是test_*

测试脚本都放在scripts目录中,scripts目录位于项目根目录下。

接口自动化框架封装的第一步统一请求方式。

5.pytest常用运行方式

找到对应的文件夹,命令运行

1. 运行当前目录下所有测试用例 : pytest

2. 详细输出(推荐用) :pytest -v

3. 只运行失败的用例 :pytest --lf

指定运行: 文件 / 函数 / 类

运行指定文件 :pytest test_login.py -v

运行文件里指定的 1 个函数 :pytest test_login.py::test_user_login -v

运行文件里指定的类 + 方法 :pytest test_order.py::TestOrder::test_create_order -v

按 标签 / 标记 运行(超级常用)

1. 运行标记为 smoke 的冒烟用例 :pytest -m=smoke -v

2. 运行非 smoke 用例 :pytest -m "not smoke" -v

3. 多标签组合 :pytest -m "smoke or order" -v

结合插件运行(企业必用)

1. 生成 Allure 报告 :pytest --alluredir=./report

2. 失败自动重跑 2 次 :pytest --reruns=2

3. 多进程并行运行(加速) :pytest -n=4

4. 生成 HTML 报告 :pytest --html=report.html

组合命令(企业真实使用)

pytest -m=smoke -v --reruns=2 --alluredir=./report

含义:

  • 运行冒烟用例
  • 详细打印
  • 失败重跑 2 次
  • 生成 allure 报告

通过 pytest.ini 运行(最规范)

项目根目录创建 pytest.ini

[pytest]
#生成allure报告
; 命令行自动带上的参数(等价于写在 main 里)
addopts = -vs --alluredir=temps --clean-alluredir
#存放测试用例的路径
testpaths = ./testcases
#定义测试文件要以test_开头
;python_files = test_*.py
python_files=test_login_ddt.py
;test_crud_disease.py
;test_crud_facility.py
#所有的测试类以Test开头
python_classes = Test*
#所有的方法以test_开头
python_functions = test_*
markers =
    smoke : 冒烟用例
    order : 工单管理

直接运行:pytest 自动按配置找用例、执行、输出。

或者:在main.py里写:

import pytest

if __name__ == '__main__':
    pytest.main()
    #pytest.main(["-v", "-m=smoke", "--alluredir=./report"])

只要你配置好了 pytest.inimain()可以空括号,运行时会自动读取 ini 里的所有配置

效果= pytest -v --alluredir=./report --reruns=2 -m=smoke

什么时候 main () 里需要写参数?

只有一种情况:想临时覆盖 ini 配置,做特殊运行。

pytest.main(["-m=order"]) # 只跑工单用例,覆盖 ini

pytest允许用命令运行,pytest是个命令工具,用自己的规则去执

行和运行代码即可。

注意:

Pytest是以方法(def 方法名)为单位发现用例的,写不写测试类根本不重要 。

在哪个目录下执行pytest就在哪个目录下按照上述规则去查找 。

以上是默认的规则,可以自定义,可以结合:pytest.ini 文件去进行配置。

命令行的运行方式中:

pytest <==> pytest.main([]) 
pytest -s <==> pytest.main(["-s"]) 
pytest -s -v <==> pytest.main(["-s","-v"]) 
pytest -sv <==> pytest.main(["-sv"]) 
pytest -vs <==> pytest.main(["-vs"]) 
pytest test_01.py::Test01::test001 -sv <==> pytest.main(["test_01.py::Test01::test001", "-vs"])

指定文件夹运行
# pytest.main(["testcase", "-vs"])
# pytest.main(["testcase/testcase01", "-vs"])
# pytest.main(["testcase/testcase01/test_04.py::test0402", "-vs"])


用例是函数,pytest解决的是:不用写调用语句,也可以执行函数内容。

-k筛选⽤例运行

在Pytest中, -k 选项⽤于通过表达式筛选和运⾏匹配的测试⽤例。使⽤ -k 选项,可

以指定⼀个字符串表达式,Pytest将根据表达式匹配测试⽤例的名称来执⾏相应的测

试。

#expression 是要匹配的测试⽤例名称的表达式。
pytest -k "expression"
运行名称包含“disease”的测试用例
pytest -k "disease" -vs<==>pytest.main(["-k","disease","vs"])

用例执行顺序

根据ASCII 码从小到大 依次执行用例。

ASCII 码大小固定规则:数字(0-9) < 大写字母(A-Z) < 小写字母(a-z)

eg: test_01_login.py → test_02_order.py → test_a_user.py → test_B_pay.py

文件内根据定义的方法的先后顺序,从上到下执行。

pytest运行时,找测试文件的顺序:
同级情况下,先找文件,再往下层找。

testcase
test_a
		test_a01.py
	test_001.py
	test_002.py

顺序运行是:test_001.py  test_002.py   test_a:test_a01.py

6.pytest命令行选项分类

Pytest 是一个流行的 Python 测试框架,它提供了许多命令行选项,可以帮助用户更

好地控制测试执行过程。在使用 pytest 进行测试时,熟悉 pytest 的命令行选项非常重要,这

将有助于减少错误和提高测试效率。

环境配置选项:这些选项用于设置 pytest 的环境配置,例如设置日志级别、覆盖配置文 件、设置测试模式等。

测试过滤选项:这些选项用于过滤和选择测试用例,例如指定测试目录、选择特定测试模 块、运行指定测试函数等。

测试执行选项:这些选项用于控制 pytest 的测试执行过程,例如重试失败测试、生成测试 报告、并行执行测试等。

7.pytest的前后置处理

在Pytest中,前置处理(setup)和后置处理(teardown)是非常重要的概念,它们用于在每

个测试用例执行前后进行一些准备工作和清理工作。Pytest提供了多种方式来处理前置和后置

操作,使得测试代码更加灵活和可维护。

前置处理(Setup)

前置处理是在测试用例执行之前执行的一些操作,通常用于准备测试环境,例如创建数据库连

接、初始化测试数据、打开文件等。在Pytest中,有几种方式可以实现前置处理:

1. setup_module或 setup :在一个模块(.py文件)中的所有测试用例执行之前只执行一次。

2. setup_function :在每个测试用例执行之前都会执行。

3. setup_class :在一个类中的所有测试用例执行之前只执行一次(需要配合 @pytest.mar

k.usefixtures 使用或使用类方法)。

4. setup_method :在一个类中的每个测试用例执行之前都会执行(需要配合类方法使用)。

后置处理(Teardown)

后置处理是在测试用例执行之后执行的一些操作,通常用于清理测试环境,例如关闭数据库连

接、删除测试数据、关闭文件等。在Pytest中,后置处理的方式与前置处理类似:

1. teardown_module 或 teardown:在一个模块(.py文件)中的所有测试用例执行之后只执行一次。

2. teardown_function :在每个测试用例(函数)执行之后都会执行。

3. teardown_class :在一个类中的所有测试用例执行之后只执行一次(需要配合类方法使

用)。

4. teardown_method :在一个类中的每个测试用例(类中的函数)执行之后都会执行(需要配合类方法使

用)。

def setup():#setup_module()
# 前置操作
  print("执⾏前置操作")

def teardown():#teardown_module()
# 后置操作
  print("执⾏后置操作")

def setup_function():
# 执行函数级别的前置操作
  print("执⾏前置操作")

def teardown_function():
# 执行函数级别的后置操作
  print("执⾏后置操作")

def test_example1():
# 测试⽤例
  print("执⾏测试⽤例test_example1")

def test_example2():
  print("执⾏测试⽤例example2")

执行结果:

class TestClass:
    @classmethod
    def setup_class(cls):
        # 类级别的前置操作
        print("执行类级别的前置操作")

    @classmethod
    def teardown_class(cls):
        # 类级别的后置操作
        print("执行类级别的后置操作")
        
    def setup_method(self, method):
      # 方法级别的前置操作
      print("执行方法级别的前置操作")

    def teardown_method(self, method):
      # 方法级别的后置操作
      print("执行方法级别的后置操作")

    def test0021(self):
        print("执行测试用例test0021")

    def test0022(self):
        print("执行测试用例test0022")

执行结果:

8.conftest的使用

例子

import pytest
from common.clean_util import clean_photos
from common.yaml_util import clean_yaml
#clean_extract在所有测试用例运行前,执行一次
@pytest.fixture(scope="session", autouse=True)
def clean_extract():
    clean_yaml()
    clean_photos()

conftest.py介绍

conftest.py配置需要注意:

conftest文件名是不能自定义的,不能更改 ;

conftest.py与运行的用例要在同一个package下,并且有init.py文件

可以放在项目的根目录下,也可以放在每个测试用例的文件夹下;

不同于普通被调用的模块,conftest.py使用时不需要导入,Pytest会首先查找这个文件;

全局的配置和前期工作都可以写在这里,放在某个包下,就是这个包数据共享的地方,

专门存放:公共 fixture、全局前置 / 后置、接口鉴权、公共配置、钩子函数

就近生效原则

  • 子目录里的 conftest.py会覆盖父目录的同名 fixture
  • 用例优先使用最近的 conftest

为什么要用?(接口自动化核心价值)

  • 抽离重复代码(登录、获取 token、读取配置、连接数据库)
  • 实现全局前置 / 后置(启动服务、清数据、关连接)
  • 统一管理接口鉴权(token、session)
  • 让测试用例只写业务逻辑,干净简洁

yield关键字作用

测试方法前要执行的或依赖的解决了,测试方法后销毁清除数据的要如何

进行呢?用yield

每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 yield 方法时从当前位置继续运行。

import pytest

# 作用域: module 表示在整个模块(即当前的test_main.py)执行前后,各执行一次
@pytest.fixture(scope="module")
def open():
    print("打开浏览器")
    yield  # 关键!这里是“分割线”
    print("执行teardown")
    print("关闭浏览器")
import pytest

def test_search1(open):
    print("test_search1")
    raise NameError  # 这里会主动抛出一个错误,让用例失败
    pass

def test_search2(open):
    print("test_search2")
    pass

def test_search3(open):
    print("test_search3")
    pass

if __name__ == '__main__':
    pytest.main(["-s"])  # -s 是为了让print语句正常输出

执行结果:

  • print("打开浏览器") → 模块开始时执行 1 次
  • test_search1 执行(不管成功失败)
  • test_search2 执行
  • test_search3 执行
  • print("执行teardown") print("关闭浏览器") → 模块结束时执行 1 次

pytest 的 fixture,默认只执行一次!

  • yield:分成 前置 + 后置 两段执行
  • 没有 yield只有前置,没有后置,执行完就结束了。

为什么没有 yield 就不会再执行?

因为:

  • yield = 让 fixture 变成 “生成器”,pytest 才能记住 “后面还有代码要执行”
  • 没有 yield = fixture 是普通函数,执行完最后一行就彻底结束,再也不会回来

@pytest.fixture()

import pytest
# @pytest.fixture 提供了全局前置、局部前置等功能,以便在测试运行前后设置所需的环境和资源.

@pytest.fixture(scope="session", autouse=True)

参数

autouse:

是否自动应用该Fixture,无需在测试用例中显式调用。

默认值:False(需要手动通过参数传递,如 def test_demo(clean_extract))。

autouse=True:无需在测试用例中声明,框架会自动运行 clean_extract。
# autouse=True:自动执行

scope:

定义fixture的作用范围,决定fixture何时创建和销毁。
#执行顺序遵循:session->package->module->class->function


# scope='function': 默认值,作用于每个测试用例(包含函数/方法),每个用例执行前都会运行一次;
# scope='class': 作用于整个类,每个测试类/测试函数执行前都会运行一次;
# scope='module': 作用于整个模块(多个类),每个module(每个py文件)执行前都会运行一次,可以实现多个.py跨文件共享前置
# scope='package': 每个python包执行前都会运行一次;
# scope='session': 作用于整个session,整个测试用例运行前运行一次
@pytest.fixture(scope="session", autouse=True)

作用域

关键字

执行时机

接口自动化使用场景

函数级

function

每条用例前

用例数据清理、独立初始化

类级

class

每个测试类前

类内用例公共准备

模块级

module

每个 py 文件前

模块公共配置

包级

package

每个包前

极少用

会话级

session

整个测试前运行一次

登录 token、全局 session、数据库连接

例子:

#clean_extract在所有测试用例运行前,执行一次
@pytest.fixture(scope="session", autouse=True)
def clean_extract():
clean_yaml()
clean_photos()

fixture带参数传递
import pytest

# autouse=True:所有用例自动使用这个 fixture,不用手动传参
@pytest.fixture(autouse=True)
def login_r(request):
    # 关键:request.param 接收 parametrize 传过来的参数
    user = request.param
    print(f"打开首页准备登录,登录用户: {user}")
    # 把用户信息返回给测试用例
    return user

import pytest

test_user_data = ["Tome", "Jerry"]

# indirect=True:告诉 pytest,把 "login_r" 这个参数交给 fixture 去执行,而不是直接传给用例
@pytest.mark.parametrize("login_r", test_user_data, indirect=True)
def test_login(login_r):
    a = login_r
    print(f"测试用例中login的返回值: {a}")
    assert a != ""

if __name__ == '__main__':
    pytest.main(["-s"])

执行结果:

打开首页准备登录,登录用户: Tome

测试用例中login的返回值: Tome .

打开首页准备登录,登录用户: Jerry

测试用例中login的返回值: Jerry .

关键点拆解:

  1. autouse=True
    • 表示这个 fixture 会被所有测试用例自动调用,不用在函数参数里写 login_r 也会执行
    • 但这里因为用了 indirect=True,它的执行会被 parametrize 控制
  1. request.param
    • 这是 pytest 内置的 request 对象提供的属性,专门用来接收 indirect=True 时,parametrize 传入的参数值
    • 可以把它理解为:parametrize 传的参数,通过 indirect 交给 fixture,fixture 用 request.param 接住

3.indirect=True 的作用(灵魂!)

  • 正常情况下,@pytest.mark.parametrize("login_r", data) 会直接把 data 里的值传给用例的 login_r 参数
  • 加上 indirect=True 后,逻辑变成:把 test_user_data 里的每个值,先传给名为 login_r 的 fixture,让 fixture 处理,再把 fixture 的返回值传给用例
  • 简单说:让 fixture 来接收参数,而不是用例直接接收

4.用例里的 login_r

  • 它不再是直接拿到 Tome/Jerry,而是拿到 login_r fixture 的返回值(这里就是 user
  • 你可以在 fixture 里加更多逻辑(比如根据用户不同执行不同登录操作),用例里只需要拿到处理好的结果。

注意:

  1. indirect=True 必须和 parametrize 的参数名与 fixture 名完全一致(这里都是 login_r
  2. request.param 只能在 fixture 里用,不能直接在测试用例里用
  3. 如果 parametrize 传多参数,需要用 indirect=["login_r"] 指定哪些参数要传给 fixture

这个玩法的核心价值(接口自动化里超实用)

可以把它升级成带参数的动态登录 fixture,比如:

@pytest.fixture
def login_r(request):
    user = request.param
    # 根据不同用户执行不同登录逻辑
    if user == "admin":
        token = admin_login()
    elif user == "test":
        token = test_user_login()
    else:
        token = guest_login()
    return {"user": user, "token": token}

pytest接口自动化框架的运行顺序

1.加载 conftest.py:Pytest会优先加载项目根目录下的 conftest.py,以及各子目录中的 conftest.py(按目录层级逐层加载)。

执行 scope="session" 的Fixture:本例中的 clean_extract 会在所有测试用例执行前运行。

2.发现测试用例:Pytest按以下规则搜索测试用例:

文件名匹配 test_*.py 或 *_test.py。

函数名或类方法名匹配 test_*。

3.执行测试用例:

默认按文件名和测试函数名的字母顺序执行。

可通过 pytest-ordering 插件自定义执行顺序。

4.生成报告:
测试完成后,生成Allure、HTML等格式的报告。

9.pytest标签

1.@pytest.mark.parametrize()

作用:

为同一个测试函数传入多组不同的参数,让测试函数自动执行多次(每组参数执行一次)。在接口自动化中,这可以用来批量测试:

  • 不同的接口请求参数(如正常 / 异常参数)
  • 不同的接口入参和预期响应结果
  • 不同的接口环境(如测试 / 预发环境)

@pytest.mark.parametrize("参数名1[,参数名2...]", 参数值列表)
def test_函数名(参数名1[,参数名2...]):
    # 测试逻辑(接口请求、断言等)
  • 参数名:字符串格式,多个参数用逗号分隔
  • 参数值列表:可迭代对象(列表 / 元组 / 字典列表/字典元祖等),每组值对应参数名的顺序

列表有几个元素,就执行几次

1.单接口用例(返回格式[{...}],执行 1 次,单组参数) :
用例是单个接口的单组参数,仅验证该接口的 “基础功能是否可用”,不做多场景覆盖,执行一次即可完成验证。

适合 “单个接口 + 单一场景 + 快速验证” 的场景,重点是 “通”,而非 “全”。

2. 数据驱动用例(返回格式[{...}, {...}, {...}],执行 N 次,多组参数) :
基于同一个接口模板,通过parametrize定义多组入参 / 预期结果,执行 N 次(N = 参数组数),覆盖该接口的 “正向 / 反向 / 边界 / 异常” 等多场景。

适合 “单个接口 + 多场景覆盖 + 减少代码冗余” 的场景,重点是 “全”,而非 “快”。

3. 流程用例(返回格式[[{}, {}, {}]],执行 1 次,流程用例列表) :

用例是多个接口按业务逻辑串联的列表,执行 1 次就能验证 “完整业务流程”,接口之间有依赖(比如前一个接口的返回值作为后一个接口的入参)。
适合 “多接口串联 + 业务流程完整验证” 的场景,重点是 “流程通顺、数据流转正确”,而非单个接口的细节。

2.skip跳过

使用场景:

调试时不想运行这个用例 ;

标记无法在某些平台上运行的测试功能 ;

在某些版本中执行,其他版本中跳过 ;

当前外部资源不可用时跳过(如果测试数据是从数据库中获取的,连接数据库未成功则跳过-因为执行会报错) ;

@pytest.mark.skip()

@pytest.mark.skip( reason="跳过原因" )# 唯一参数,可选

@pytest.mark.skip(reason="功能未开发,暂不执行")
def test_01():
    pass
@pytest.mark.skipif()

@pytest.mark.skipif( condition, # 条件:True=跳过,False=执行

reason="" # 跳过原因(强烈建议必须写) )

# 条件为 True → 跳过
@pytest.mark.skipif(1 == 1, reason="条件满足,跳过用例")
def test_skip():
    pass
import pytest
env = "dev"

@pytest.mark.skipif(env == "dev", reason="开发环境跳过")
def test_api():
    pass

装饰器

作用

使用场景

@pytest.mark.skip

永远跳过,无任何判断

功能废弃、代码未写完、永久不用跑

@pytest.mark.skipif

满足条件才跳过

区分环境、版本兼容、临时屏蔽某版本用例

  • skip无条件跳过;skipif条件式跳过
  • 可以装饰函数、类
  • 可以用 pytest.skip()用例内部动态跳过
  • skipif 常用:判断系统、环境变量、版本号控制用例执行

扩展:pytest.skip () 函数内部跳过(参数更多)

这个是在代码内部动态跳过,不是装饰器,参数更多

pytest.skip( reason="跳过原因",

allow_module_level=False # 是否允许在模块级别跳过(整个文件跳过) )

def test_demo():
    if 1 > 0:
        pytest.skip(reason="内部条件满足,直接跳过")

3.xfail标记用例为失败

pytest.mark.xfail(
    condition=None,    # 条件:满足就标记预期失败
    *,
    reason=None,       # 为什么预期失败
    raises=None,       # 只预期某种异常
    run=True,          # 是否运行这个用例
    strict=False       # 严格模式:意外成功是否算失败
)

xfail = 预期失败(expected failure)

  • 标记这个用例本来就应该失败
  • 就算真的失败了 → 也算通过(XFAIL)
  • 万一意外成功了 → 算失败(XPASS)

使用场景

  • 功能没做完
  • Bug 没修复
  • 接口暂不可用
  • 已知会失败,但不想让它影响整体报告

4.使用自定义标记mark,只执行部分用例

import pytest

def pytest_configure(config):
    marker_list = ["search", "login"]  # 标签名集合
    for markers in marker_list:
        config.addinivalue_line("markers", markers)

或者在ini文件中声明

[pytest] 
markers =
 login: Run login tests
 search: Run search tests
 smoke: 冒烟用例
import pytest

@pytest.mark.login
def test_login():
    print("登录用例")

@pytest.mark.search
def test_search1():
    print("搜索用例1")

@pytest.mark.search
def test_search2():
    print("搜索用例2")

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

1. 核心钩子函数 pytest_configure

  • 这是 pytest 的内置钩子函数,在 pytest 启动时自动执行一次
  • 作用:用来做全局配置,比如注册自定义标记、设置全局变量等

2. config.addinivalue_line("markers", markers)

  • 这是 pytest 注册自定义标记的标准方法
  • 语法:config.addinivalue_line("markers", "标记名: 标记描述")
  • 作用:告诉 pytest,loginsearch 是合法的标记,不会报 Unknown marker 警告

3.@pytest.mark.login / @pytest.mark.search

  • 给用例打上对应的标签
  • 打了 @pytest.mark.login 的用例,就属于 login 分组
  • 打了 @pytest.mark.search 的用例,就属于 search 分组

用这些标记跑用例 (标签支持逻辑筛选)

1>只跑 login 标记的用例 : pytest test_main.py -m login -sv/pytest.main(["-vs","-m","login"])

只会执行 test_login,输出: 登录用例

2>同时跑 login 和 search 标记的用例(或条件) pytest test_main.py -m "login or search" -s

3>只跑 login 且不跑 search 的用例 pytest test_main.py -m "login and not search" -s

注册标记的标准写法(推荐)

现在的写法虽然能用,但更规范的写法要加上标记描述,方便团队协作:

def pytest_configure(config):
    marker_list = [
        ("login", "标记登录相关用例"),
        ("search", "标记搜索相关用例")
    ]
    for name, desc in marker_list:
        config.addinivalue_line("markers", f"{name}: {desc}")

这样运行 pytest --markers 时,就能看到每个标记的说明:

@login: 标记登录相关用例

@search: 标记搜索相关用例

核心价值(接口自动化里超实用)

  1. 用例分组管理:按模块(登录 / 搜索 / 订单)、优先级(p0/p1)、环境(dev/test)给用例打标签
  2. 灵活执行策略:上线前只跑核心 p0 用例,回归时跑全量,问题排查时只跑指定模块
  3. 避免警告:不注册标记直接用,pytest 会报 Unknown marker 警告,注册后警告消失

避坑提醒

  1. 标记名不能重复:同一个标记名只能注册一次,重复注册会报错
  2. 标记名不能有特殊字符:只能用字母、数字、下划线
  3. 运行时引号问题:带逻辑运算符(or/and/not)的 -m 参数,要用双引号包起来,比如 -m "login or search"

钩子函数

pytest 钩子函数 = pytest 框架给你预留的 “后门”,让你可以在 pytest 的不同阶段,插入自己的代码, 实现 pytest 本身不支持的功能。

pytest 在运行测试的过程中,会按固定的流程走(比如:启动 → 收集用例 → 执行用例 → 生成报告)。而「钩子函数」就是你能在这些流程节点上,“插队” 执行自己代码的入口。

pytest_configure 就是其中一个钩子函数。

钩子函数的核心特点

  1. 不用手动调用:pytest 会在固定时机自动执行,你只需要按规定的名字写好函数就行
  2. 名字固定:不能自己随便起名,必须用 pytest 规定的名字(比如 pytest_configure
  3. 参数固定:每个钩子函数的参数都是 pytest 传进来的,比如 configsessionitems,用来和 pytest 交互
  4. 优先级高:钩子函数的代码会在 pytest 的核心流程之前 / 之后执行,能影响 pytest 的行为

钩子函数

执行时机

接口自动化常用场景

pytest_configure(config)

pytest 启动时,自动执行一次

注册自定义标记、读取全局配置、设置环境变量

pytest_collection_modifyitems(items)

收集完所有用例后

修改用例名称(解决中文乱码)、给用例排序

pytest_runtest_makereport(item, call)

每个用例执行前后

记录用例执行结果、失败时自动截图 / 保存日志

pytest_sessionstart(session)

整个测试会话开始前

初始化全局资源(比如连接数据库、启动服务)

pytest_sessionfinish(session)

整个测试会话结束后

关闭数据库连接、清理测试数据、生成最终报告

pytest_runtest_setup(item)

每个用例执行前

用例级前置操作(比如创建测试数据)

pytest_runtest_teardown(item)

每个用例执行后

用例级后置操作(比如删除测试数据)

和 fixture 的区别

对比项

钩子函数

fixture

作用对象

pytest 框架本身

测试用例

执行时机

pytest 运行的固定节点(启动、收集用例、会话结束等)

按作用域(function/module/session),在用例前后执行

核心用途

修改 pytest 行为、全局配置、报告处理

给用例提供前置 / 后置、依赖注入(比如 token、session)

调用方式

自动执行,不用传参

必须作为参数传入用例函数,或者用 autouse=True

10.pytest的ini文件

pytest.ini介绍

是pytest主配置文件,一般放在项目根目录下,名字不可改,用于存放一些配置选项,这些选项可以通过pytest -h查看到。

ini文件由节,键,值组成。

节[section]

选项/参数(键=值)

name=value

注释

注释使用分号(;)

[pytest]
;生成allure报告
;-v:详情信息
;-s:调试信息
;--alluredir=temps:生成临时报告
;--clean-alluredir:清空临时文件
addopt = -vs --alluredir=temps --clean-alluredir

;指定存放测试用例的路径
;testpaths是一系列相对于根目录的路径,用于限定测试用例的搜索范围
testpaths = ./testcases

;定义测试文件以test_开头
python_files = test_*.py

;定义测试类以Test开头
pyton_classes = Test*.py

;定义测试方法以test_开头
python_functions = test_*

;用于注册自定义的测试标记(用于对测试函数或类进行分类和过滤的机制)
;定义后,就可以在测试代码中使用@pytest.mark.smoke来标记测试函数或类。多个换行
markers =
    smoke: 冒烟用例

;日志配置
;日志文件
log_file = ./logs/frame.log

;日志的级别:DEBUG调试,INFO信息,WARNING警告,ERROR错误,CRITIAL严重
log_file_level = INFO

;日志格式
;设置了日志文件的格式,包括时间戳、日志级别、文件名、日志消息。
log_file_format = %(asctime)s %(levelname)s %(filename)s %(message)s

;基础路径配置
[base_url]
base_url = https://test.sztdri.com

pytest.ini ⽂件中的选项和参数设置将适⽤于整个测试项⽬,但也可以在

命令⾏中使⽤选项覆盖部分配置。

ini文件中自定义测试标记和在conftest文件中用pytest_configure注册标记区别:

它们的最终效果是一样的:都是告诉 pytest “这些标记是合法的,别报 Unknown marker 警告”。

• pytest.ini 方式:在配置文件里写死标记

• conftest.py + pytest_configure 方式:用代码动态注册标记

对比维度

pytest.ini 方式

conftest.py + pytest_configure 方式

写法

静态配置文件

动态代码注册

语法

固定格式 markers =

下写多行

config.addinivalue_line("markers", "name: desc")

适用场景

标记固定不变的项目

标记需要动态生成、多环境区分、条件注册

扩展性

差,只能写死

强,支持循环、条件判断、动态读取配置

多项目 / 多环境兼容

差,每个项目都要维护 ini

强,可复用代码、按环境注册不同标记

修改方式

手动改 ini 文件

改代码逻辑,支持配置文件驱动

团队协作友好度

一般,需要维护 ini 格式

高,可统一管理、自动生成

执行时机

pytest 启动时读取配置

pytest 启动时自动执行钩子注册


 

Logo

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

更多推荐