1.简介
pytest是一个基于Python的测试框架,广泛用于编写简单且可扩展的测试代码。它支持单元测试、功能测试和集成测试,适用于从简单脚本到复杂应用程序的测试场景。
2.安装
pip install pytest==8.3.2
pytest 版本 | 最低 Python 版本 |
8.0+ | 3.8+ |
7.1+ | 3.7+ |
6.2 - 7.0 | 3.6+ |
5.0 - 6.1 | 3.5+ |
3.3 - 4.6 | 2.7, 3.4+ |
注意自己的python版本是否支持要下载的pytest版本
3.用例运行规则
安装完pytest后,在已test开头的方法前面会出现这个运行的图标
规则:
1. ⽂件名必须以 test_ 开头或者 _test 结尾
2. 测试类必须以 Test 开头,并且不能有 __init__ ⽅法。3. 测试⽅法必须以 test 开头
满足上述规则的用例,我们在终端输入pytest就会自动执行这些用例
注意:测试的Python类中不可以添加init⽅法
pytest 在实例化测试类时,默认会调用无参数的构造函数。如果测试类中定义了
__init__
方法,可能会导致 pytest 无法正确初始化测试类,从而引发错误。测试类的主要目的是定义测试方法,而不是管理复杂的初始化逻辑。pytest 提供了其他机制(如 fixture)来处理初始化需求。
4.前后置
使用 setup_method
或 setup_class
方法来替代 __init__
方法。这些方法是 pytest 推荐的初始化方式,会在测试运行前自动调用。
pytest 框架提供三种⽅法做前后置的操作:
• setup_method 和 teardown_method :这两个⽅法⽤于类中的每个测试⽅法的前置和后置操
作。
• setup_class 和 teardown_class :这两个⽅法⽤于整个测试类的前置和后置操作。
• fixture :这是 pytest 推荐的⽅式来实现测试⽤例的前置和后置操作。 fixture 提供了更
灵活的控制和更强⼤的功能。(fixture将在后面单独讲解)
setup_method 和 teardown_method 会在每个方法执行前后都执行一遍
setup_class 和 teardown_class 会在本类的所有测试方法执行前执行一次,执行后执行一次,但是不能跨测试类执行(fixture能够解决这个问题)
5.pytest命令参数
基础运行参数
-v
或--verbose
: 显示详细测试结果,包括每个测试用例的名称和状态。-q
或--quiet
: 简化输出,仅显示总体结果。-k <表达式>
: 通过关键字表达式筛选测试用例。例如pytest -k "test_login"
仅运行名称包含test_login
的测试。-m <标记>
: 运行带有特定标记的测试用例。例如pytest -m smoke
仅运行标记为smoke
的测试。测试范围控制
::
指定特定测试类或方法。例如pytest tests/test_module.py::TestClass::test_method
仅运行指定方法。--collect-only
: 仅列出可用的测试用例但不执行。--lf
或--last-failed
: 仅重新运行上次失败的测试。--ff
或--failed-first
: 先运行上次失败的测试,再运行其他测试。输出与报告
--tb=<style>
: 设置错误回溯格式。可选值:auto
(默认)、long
、short
、no
、line
、native
。-r <chars>
: 显示额外摘要信息。例如-rA
显示所有结果,-rE
显示错误。--junitxml=<path>
: 生成 JUnit 格式的 XML 报告。--html=<path>
: 生成 HTML 报告(需安装pytest-html
插件)。调试与诊断
-s
: 禁用捕获,直接输出打印内容(相当于--capture=no
)。--pdb
: 在测试失败时自动进入调试模式。--setup-show
: 显示测试的 fixture 执行过程。--durations=<N>
: 显示最慢的 N 个测试用例的执行时间。其他实用参数
-x
: 遇到第一个失败时立即停止测试。--maxfail=<num>
: 允许的最大失败次数,达到后停止测试。--cov=<path>
: 生成代码覆盖率报告(需安装pytest-cov
插件)。-n <num>
: 使用多进程并行运行测试(需安装pytest-xdist
插件)。
6.pytest配置文件
在当前项⽬下创建 pytest.ini ⽂件,pytest.ini作用于同级目录及其子目录,该⽂件为 pytest 的配置⽂件,以下为常⻅的配置选项:
参数 | 解释 |
addopts | 指定在命令⾏中默认包含的选项。 |
testpaths | 指定搜索测试的⽬录。 |
python_files | 指定发现测试模块时使⽤的⽂件匹配模式。 |
python_classes | 指定发现测试类时使⽤的类名前缀或模式。 |
python_functions | 指定发现测试函数和⽅法时使⽤的函数名前缀或模式。 |
norecursedirs | 指定在搜索测试时应该避免递归进⼊的⽬录模式。 |
markers | 定义测试标记,⽤于标记测试⽤例。 |
[pytest]
addopts = -vs
testpaths = ./cases
python_files = test_*.py
python_classes = Test*
7.断言
断⾔( assert )是⼀种调试辅助⼯具,⽤于检查程序的状态是否符合预期。如果断⾔失败(即条件为假),Python解释器将抛出⼀个 AssertionError 异常。断⾔通常⽤于检测程序中的逻辑错误。pytest 允许你在 Python 测试中使⽤标准的 Python assert语句来验证预期和值。
语法:
assert 条件,错误信息
• 条件 :必须是⼀个布尔表达式。
• 错误信息 :当条件为假时显⽰的错误信息,可选。
8.参数化
参数化设计是⾃动化设计中的⼀个重要组成部分,它通过定义设计参数和规则,使得设计过程更加灵活和可控。
pytest中内置的 pytest.mark.parametrize 装饰器允许对测试函数的参数进⾏参数化。
在用例上使用参数化
在类上使用参数化:
@pytest.mark.parametrize("test_input,expected",[("3+2",5),("22*2",44),("3-2",1),("5/2",2.5)])
class Test_01:
def test_01(self,test_input,expected):
assert eval(test_input) == expected
def test_02(self,test_input,expected):
assert eval(test_input) == expected
def test_03(self,test_input,expected):
assert eval(test_input) == expected
对模块中所有测试进行参数化
import pytest
pytestmark = pytest.mark.parametrize("test_input,expected",[("3+2",5),("22*2",44),("3-2",1),("5/2",2.5)])
class Test_01:
def test_01(self,test_input,expected):
assert eval(test_input) == expected
def test_02(self,test_input,expected):
assert eval(test_input) == expected
def test_03(self,test_input,expected):
assert eval(test_input) == expected
class Test_02:
def test_04(self,test_input,expected):
assert eval(test_input) == expected
def test_05(self,test_input,expected):
assert eval(test_input) == expected
def test_06(self,test_input,expected):
assert eval(test_input) == expected
9.fixture
概念:
Fixture 是测试框架中的一个重要概念,用于在测试运行前准备所需的数据或环境,并在测试完成后进行清理。Fixture 可以确保测试在一致和可控的条件下运行,提高测试的可靠性和可维护性。
主要作用:
Fixture 通常用于以下几种场景:
- 数据库初始化:例如创建测试表、插入测试数据。
- 文件系统准备:例如创建临时文件或目录。
- 网络请求模拟:例如模拟 API 返回数据。
- 资源管理:例如打开和关闭数据库连接。
fixture基本使用一:
def fixture_01():
print("fixture_01")
def test_fixture_01():
fixture_01()
print("test_fixture_01")
@pytest.fixture
def fixture_02():
print("fixture_02")
def test_fixture_02(fixture_02):
print("test_fixture_02")
未标记 fixture ⽅法的调⽤与 fixture 标记的⽅法调⽤完全不⼀样,前者需要在⽅法体中调⽤,
⽽后者可以将函数名作为参数进⾏调⽤。测试脚本中存在的很多重复的代码、公共的数据对象时,使⽤ fixture 最为合适
fixture基本使用二:
访问列表⻚和详情⻚之前都需要执⾏登录操作,相当于前置
import pytest
@pytest.fixture
def login():
print("---执⾏登陆操作-----")
def test_list(login):
print("---访问列表⻚")
def test_detail(login):
print("---访问详情⻚")
fixture嵌套:
import pytest
@pytest.fixture
def first_entry():
return "first"
@pytest.fixture
def second_entry(first_entry):
return [first_entry]
def test_01(second_entry):
second_entry.append("second_entry")
print(second_entry)
测试不必局限于单个 fixture ,它们可以依赖于您想要的任意数量的 fixture ,并且fixture 也可以使⽤其他 fixture 。 pytest 最伟⼤的优势之⼀是其极其灵活的 fixture 系统,它允许我们将测试的复杂需求简化为更简单和有组织的函数,我们只需要每个函数描述它们所依赖的事物
请求多个fixture
- fixture 的执行顺序由 pytest 自动管理,通常按依赖关系和定义顺序执行。
yield fixture:
在 pytest 中,
yield fixture
是一种通过生成器(yield
)机制管理资源的 fixture。它允许在测试执行前进行初始化操作,并在测试完成后自动执行清理操作,适合处理需要释放的资源(如数据库连接、文件句柄等)。
yield相当于return ,但又不太一样,在测试用例执行完毕后,会回来执行yield下面的代码
创建⽂件句柄与关闭⽂件:
import pytest
@pytest.fixture
def file_read():
print("打开⽂件句柄")
fo = open("test.txt", "r")
yield fo
print("关闭打开的⽂件")
fo.close()
def file_write():
print("打开⽂件句柄")
fo = open("test.txt","w",encoding="utf-8")
return fo
def test_file(file_write, file_read):
# 写⼊数据
w = file_write
w.write("测试数据")
w.close() # 写⼊后关闭⽂件句柄,以便读取
# 读取数据
r = file_read
str = r.read(10)
print("⽂件内容:", str)
带参数的fixture:
参数详解:
• scope 参数⽤于控制fixture的作⽤范围,决定了fixture的⽣命周期。可选值有:
◦ function (默认):每个测试函数都会调⽤⼀次fixture。
◦ class :在同⼀个测试类中共享这个fixture。
◦ module :在同⼀个测试模块中共享这个fixture。(⼀个⽂件⾥)
◦ session :整个测试会话中共享这个fixture。 • autouse 参数默认为 False 。如果设置为 True ,则每个测试函数都会⾃动调⽤该fixture,
⽆需显式传⼊ • params 参数⽤于参数化fixture,⽀持列表传⼊。每个参数值都会使fixture执⾏⼀次,类似于for循环 • ids 参数与 params 配合使⽤,为每个参数化实例指定可读的标识符(给参数取名字) • name 参数⽤于为fixture显式设置⼀个名称。如果使⽤了 name ,则在测试函数中需要使⽤这个名称来引⽤ fixture (给fixture取名字)
1.scope
scope="function"
每个测试用例运行前都会调用
fixture默认是funtions,每个方法
scope="class"
在同⼀个测试类中共享这个fixture。
scope="session"
- 作用域:fixture 在整个模块(即一个
.py
文件)中只执行一次。- 用途:适合在模块级别进行初始化和清理操作,例如打开数据库连接、设置全局配置等。
#conftest.py import pytest @pytest.fixture(scope="module") def module_fixture(): print("\n模块级别的前置操作") yield print("\n模块级别的后置操作")
#作用于整个test_module.py def test_module1(module_fixture): print("test_module1") def test_module2(module_fixture): print("test_module2")
scope="session"
- 作用域:fixture 在整个测试会话(即所有测试文件)中只执行一次。
- 用途:适合在会话级别进行初始化和清理操作,例如启动浏览器、连接数据库等。
#conftest.py import pytest @pytest.fixture(scope="session") def module_fixture(): print("\n模块级别的前置操作") yield print("\n模块级别的后置操作")
#test_module1.py def test_module1(module_fixture): print("test_module11") def test_module2(module_fixture): print("test_module12")
#test_module2.py def test_module1(module_fixture): print("test_module21") def test_module2(module_fixture): print("test_module22")
2.autose
autouse 默认为 False ,即当前的 fixture 需要⼿动显⽰调⽤,在该案例之前我们默认使⽤的
都是 autouse=False
当 autouse=True 时, fixture 会在所有测试函数执⾏之前⾃动调⽤,⽆论这些测试函数是否显式地引⽤了该 fixture
3.params
- 作用:用于参数化 fixture,支持传入一个列表。每个参数值都会使 fixture 执行一次,类似于
for
循环。- 用途:适合在不同参数下重复执行同一个 fixture。
import pytest # 定义⼀个参数化的 fixture @pytest.fixture(params=["a", "b"]) def data_provider(request): return request.param # 定义⼀个测试函数,它依赖于上⾯的参数化 fixture def test_data(data_provider): assert data_provider != None print(f"Testing with data provider: {data_provider}")
4.ids
- 作用:与
params
配合使用,为每个参数化实例指定可读的标识符(给参数取名字)。- 用途:提高测试结果的可读性,特别是在参数较多时。就是告诉我们用的是那个参数,给参数取的名字
5.name
- 作用:用于为 fixture 显式设置一个名称。如果使用了
name
,则在测试函数中需要使用这个名称来引用 fixture。- 用途:避免 fixture 名称与测试函数中的变量名冲突,或者为 fixture 取一个更具描述性的名称。
- name相当于改名卡,使用后原来的名字就不能用了