当前位置: 首页 > news >正文

快速上手pytest

1. pytest的基础

1.1 什么是pytest 

pytest 是 python 中的一个测试框架,用于编写简洁、可扩展的测试代码,帮助开发者验证结果是否与预期相符。

python 中有很多的测试框架,那我们为什么要学习 pytest 呢? 

pytest 的优势: 

  • 不需要复杂的类继承(不像 unittest)
  • 支持断言直接使用 python 的 assert 语句
  • 提供丰富的插件生态(如生成 HTML 报告、并行测试等) 

1.2 pytest的安装 

pytest 属于 python 的第三方库(不属于标准库),所有在使用之前需要在 terminal 中进行安装 

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

 后面指定的是国内源下载,能够提高下载的速度

1.3 第一个测试例子 

创建一个文件:test_first.py 

python文件内容为: 

def add(num1, num2):return num1 + num2def test_add():assert add(1, 2) == 3

运行代码:pytest test_first.py

2. pytest的核心功能 

2.1 测试发现规则 

pytest 会自动发现以下的文件: 

  • 文件名以 test_ 开头 或 _test结尾(如 test_first.py 或 first_test.py)
  • 类名以 Test 开头
  • 函数名以 test_开头 

文件名符合规则,是 pytest 处理文件的前提条件。类/函数符合规则,是执行具体测试的必要条件。 

若文件名符合规则,类符合规则,类中的函数不符合规则,则会跳过类中的不符合规则的函数。除非在符合规则的函数里面调用不符合规则的函数。 

符合规则的文件+类+不符合规则的函数  :

class TestAdd:def add(self):print(1 + 1)

运行结果:

符合规则的文件+类+函数:

class TestAdd:def test_add(self):print(1 + 1)

运行结果: 

2.2 运行测试的常用方式 

运行所有的测试:pytest

运行指定文件:pytest 文件名(符合规则) 

运行指定目录:pytest 目录/ 

运行包含关键字的测试:pytest -k "add"  #运行名称关于add的测试 

遇到错误时停止:pytest --pdb  #进入调试模式 

2.3 断言(assert) 

直接使用 python 的 assert 语句。 

断言的作用:判断实际结果是否与预期结果一致。

在 pytest 中运行测试时,控制台会通过特定符号直观反馈每个测试用例的执行结果。

以下是常见的符号及其含义: 

符号含义触发场景
.测试通过断言成功且无异常
F测试失败断言失败(如 assert 1 == 2)
E代码错误测试代码本身抛出异常(非断言失败)
s测试跳过使用 @pytest.mark.skip 主动跳过测试
x预期失败(XFAIL)测试标记为预期失败且确实失败
X意外通过(XPASS)测试标记为预期失败但实际通过了
w警告代码触发了警告(如弃用函数)

2.4 夹具(Fixture) 

2.4.1 夹具的作用与使用 

夹具的作用:为测试用例提供预置的环境和资源,避免重复写相同的准备代码,让测试更简洁、可维护。

  • 数据库连接
  • 临时文件
  • 模拟对象
  • 共享配置 

举个例子:假设你要测试“煮不同口味的面条” 

没有夹具:每次煮面都要自己拆包装、烧水、煮面、吃完洗碗......重复劳动。

有夹具:小助手自动帮你完成这些事:提前烧好一锅水(前置准备)、煮好面递给你(提供资源)、你吃完后,它自动洗碗(清理垃圾)。你只需要专注测试“面的味道”,其他杂活都交给它。

夹具的定义:用@pytest.fixture装饰器定义一个夹具 

写一个简单的代码(可能与实际业务不符),方便大家理解: 

@pytest.fixture
def user():return "Alice"

夹具的使用:在测试函数中将夹具作为参数传入即可使用 

def test_user(user):print(user)

运行结果: 

2.4.2 夹具的作用范围(scope) 

通过 scope 参数控制夹具的作用域: 

  • function(默认):每个测试函数执行一次 
  • class:每个测试类执行一次
  • module:每个 python 文件执行一次
  • session:整个 pytest 运行过程执行一次 

大家看到上面的夹具作用域,可能就有点懵了。可能不明白什么叫做执行一次,接下来就给大家深入解析一下: 

并不是说它们只能执行一次,而是说当设置不同的scope参数内容时,它们的作用域是不同的。 

作用域为function的代码理解: 

import pytestclass TestFruit:@pytest.fixture(scope="function")def ret_fruit(self):return {"A": "apple","P": "pear","W": "watermelon"}def test_fruit1(self, ret_fruit):ret_fruit["A"] = "avocado"print(ret_fruit)def test_fruit2(self, ret_fruit):print(ret_fruit)

 运行结果:

分析:

夹具作用域(scope="function"):

  • 规则:夹具 ret_fruit 在 每个测试函数执行前初始化一次,每个测试函数获得独立的字典对象。
  • 初始化:每个测试函数(如:test_fruit1、test_fruit2)执行前,都会调用一次 ret_fruit,生成新字典。
  • 清理:每个测试函数结束后清理资源。
  • 隔离性:每个测试函数操作的字典是完全独立的对象,互不干扰。

字典虽然是可变的,但 ret_fruit 夹具 每次返回一个新字典。所以在 test_fruit1 中修改字典,不会影响 test_fruit2,因为它们操作的是不同的对象。

作用域为 class 的代码理解: 

import pytestclass TestFruit:@pytest.fixture(scope="class")def ret_fruit(self):return {"A": "apple","P": "pear","W": "watermelon"}def test_fruit1(self, ret_fruit):ret_fruit["A"] = "avocado"print(ret_fruit)def test_fruit2(self, ret_fruit):print(ret_fruit)

运行结果: 

分析: 

夹具的作用域(scope="class"):

  • 规则:夹具 ret_fruit 在测试类的 第一个测试方法执行前初始化一次,后续所有测试方法共享同一个对象。
  • 初始化:在 TestFruit 类的第一个测试方法(如 test_fruit1)执行前生成字典。
  • 清理:在 TestFruit 类的最后一个测试方法执行后清理。
  • 共享性:类内的所有测试方法(如 test_fruit1 和 test_fruit2)共享同一个字典对象。 

字典是可变对象,在 test_fruit1 中修改 ret_fruit["A"] = "avocado" 会直接影响 test_fruit2 中接收的 ret_fruit,因为 ret_fruit 是同一个字典的引用。 

2.5 pytest的参数化 

2.5.1 什么是参数化测试? 

参数化测试:就是用不同测试数据,去反复运行同一个测试函数,避免重复写类似的测试代码。

举例说明:学过测试的小伙伴应该知道,当我们去测一个登录功能的时候,是会有很多条测试用例去测这个登录功能的。那我们进行登录自动化接口测试的时候,那么登录功能就会被抽象成一个函数,测这个功能的测试数据又有很多。如果每个情况都写一个测试函数,那么代码就会很冗余。参数化就可以让你用一个函数跑遍所有情况。

2.5.2 参数化的使用 

参数化的使用:@pytest.mark.parametrize("参数名", 参数值列表) 

import pytestdef add(a, b):return a + b# 装饰器写法:@pytest.mark.parametrize("参数名", 参数值列表)
@pytest.mark.parametrize("a, b, expected", [(1, 2, 3), (2, 3, 5)])
def test_add(a, b, expected):assert add(a, b) == expected

 运行结果:

@pytest.mark.parametrize:这是 pytest 提供的参数化装饰器。 

第一个参数:“参数名” 是字符串,多个参数用逗号隔开(如 "a, b, expected")。

第二个参数:是一个列表,列表中的每个元素是一组参数值(如 (1, 2, 3)对应一次测试)。

测试函数参数:参数名必须和装饰器中的参数名一一对应(如 a, b, expected)。

上述代码执行测试时,pytest会自动生成 2 个测试用例:

  • 用 a = 1, b = 2, expected = 3 运行一次 test_add。
  • 用 a = 2, b = 3, expected = 5 运行一次 test_add。

注意:实际中参数值列表部分,通常会采用调用其他函数(获取文件数据函数)获取对应的返回值,来获取参数值列表部分。这样就能够实现代码与数据的分离。

3. pytest的高级功能 

3.1 标记(Mark) 

3.1.1 跳过测试 

跳过测试:@pytest.mark.skip 和 @pytest.mark.skipif 

作用:在某些条件下跳过测试,避免执行不必要或暂时无法运行的测试。 

直接跳过: 

import pytest@pytest.mark.skip(reason="该功能尚未实现")
def test_feature_not_implemented():assert False  # 不会执行

运行结果: 

动态条件跳过: 

import pytest@pytest.mark.skipif(1 == 1, reason="条件成立跳过")
def test_condition_skipping():pass

运行结果: 

这个只有条件成立才会跳过,不成立则不会跳过。

3.1.2 预期失败 

预期失败:@pytest.mark.xfail 

作用:标记已知会失败的测试,测试失败时不计入错误统计(显示为 xfailed) 

import pytest@pytest.mark.xfail
def test_beta_feature():assert False

运行结果: 

3.2 异常测试 

作用:验证代码是否抛出预期的异常类型和错误信息 

基础异常断言:pytest.raises  

import pytestdef test_divide_by_zero():with pytest.raises(ZeroDivisionError):10 / 0

 结果抛出 ZeroDivisionError,才能测试通过。

运行结果: 

装饰器方式:@pytest.mark.xfail(raises=...) 

适用于整个测试函数都预期抛出异常的场景: 

import pytest@pytest.mark.xfail(raises=IndexError)
def test_out_of_bounds():my_list = [1, 2, 3]return my_list[100]  # 预期会抛出IndexError

运行结果: 

相关文章:

  • 【SAP FICO】在建工程及固定资产
  • [Java 基础]变量,装东西的盒子
  • Linux 下 ChromeDriver 安装
  • Redisson学习专栏(五):源码阅读及Redisson的Netty通信层设计
  • 【分布式技术】KeepAlived高可用架构科普
  • 系统架构设计论文
  • 3.2 HarmonyOS NEXT跨设备任务调度与协同实战:算力分配、音视频协同与智能家居联动
  • P1438 无聊的数列/P1253 扶苏的问题
  • 【自动思考记忆系统】demo (Java版)
  • Day11
  • S1240拨打电话时的工作过程
  • Rust 学习笔记:关于 Cargo 的练习题
  • 如何监测光伏系统中的电能质量问题?分布式光伏电能质量解决方案
  • [Java 基础]选英雄(配置 IDEA)
  • 第十三章 Java基础-特殊处理
  • C++核心编程_ 函数调用运算符重载
  • 构建基于深度学习的人体姿态估计系统 数据预处理到模型训练、评估和部署 _如何利用人体姿态识别估计数据集_数据进行人体姿态估计研究的建议Human3.6M
  • MySQL 8 完整安装指南(Ubuntu 22.04)
  • 【2025RAG最新进展】
  • 数据结构:递归的种类(Types of Recursion)
  • 怎么建设自己淘宝网站/人力资源培训网
  • 网站源码建站教程/项目网站
  • 求推荐好玩的网页游戏/鹤壁seo公司
  • 旅游网站品牌建设/网络营销战略
  • c 做网站/seo什么意思简单来说
  • 泉山徐州网站开发/国外网络推广