Python + Pytest 测试框架——数据驱动
off999 2025-07-28 19:39 66 浏览 0 评论
引言
前面已经和大家介绍过 Unittest 测试框架的数据驱动框架 DDT,以及其实现原理。今天和大家分享的是 Pytest 测试框架的数据驱动,Pytest 测试框架的数据驱动是由 pytest 自带的pytest.mark.parametrize()来实现的。
pytest.mark.parametrize 实现数据驱动
pytest.mark.parametrize 是 pytest 的内置装饰器,它允许你在 function 或者 class 上定义多组参数和 fixture 来实现数据驱动。
@pytest.mark.parametrize() 装饰器接收两个参数:
第一个参数以字符串的形式存在,它代表能被被测试函数所能接受的参数,如果被测试函数有多个参数,则以逗号分隔;
第二个参数用于保存测试数据。如果只有一组数据,以列表的形式存在,如果有多组数据,以列表嵌套元组的形式存在(例如:[1,1]或者[(1,1), (2,2)])。
针对装饰器的单参数和多参数,分别举例如下。
1.pytest.mark.parametrize 单参数
# test_singal.py
import pytest
@pytest.mark.parametrize("number", [1, 0])
def test_equal(number):
assert number == 1
if __name__ == "__main__":
pytest.main([])以上是单参数的一个例子,在这个例子中,test_equal 函数接收一个参数 number,这个参数有两组数据,分别是 1 和 0。
tips:
装饰器 pytest.mark.parametrize 的第一个参数里的参数名称必须与测试函数中的参数称保持一致。
即:test_equal这个函数方法的参数 number 必须与装饰器里的第一个参数的名称 number 保持一致。
运行以上代码,结果如下图所示:
可以看到,函数 test_equal 提供了两组参数 1 和 0,所以它也执行了 2 次。
2.pytest.mark.parametrize 多参数
pytest.mark.parametrize 不仅支持单个参数,也可以支持多个参数,多个参数比较常见,因为在日常工作中,我们提供测试数据,不仅仅包括用于测试的数据,还包括用于验证的数据,所以多参数还是比较常见的。
pytest.mark.parametrize 可以支持多参数,举例如下:
# test_baidu.py
import time
import pytest
from selenium import webdriver
@pytest.mark.baidu
class TestBaidu:
def setup_method(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(30)
self.base_url = "http://www.baidu.com/"
@pytest.mark.parametrize('search_string, expect_string', [('Testing', 'Testing'), ('helloworld.com', 'Testing')])
def test_baidu_search(self, search_string, expect_string):
driver = self.driver
driver.get(self.base_url + "/")
driver.find_element_by_id("kw").send_keys(search_string)
driver.find_element_by_id("su").click()
time.sleep(2)
search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')
assert (expect_string in search_results) is True
def teardown_method(self):
self.driver.quit()
if __name__ == "__main__":
pytest.main(["-m", "baidu", "-s", "-v", "-k", "test_baidu_search", "test_baidu.py"])上面这段代码,被测试函数 test_baidu_search 有两个参数,分别是 search_string 和 expect_string。那么对应着,在 pytest.mark.parametrize 这个装饰器的第一个参数里,也包含 search_string 和 expect_string。
在命令行中运行结果如下:
pytest.fixture 扩展数据驱动
做过自动化测试的小伙伴,应该都很清楚地知道,无论 API 还是 UI 的自动化测试可以总结为三个步骤:
测试前的准备 —> 执行测试 —> 测试后的清理。
在日常的测试中,测试前的准备通常就是测试需要的前置条件,它可以是简单的登录操作、联合查询数据库操作、测试数据读取准备操作,甚至是逻辑复杂的函数操作。
和 unittest 框架一样,在 pytest 中也可以通过使用 setup 和 teardown 来完成测试前置工作。
例如:
使用 setup_method、setup_class、setup_module 来分别完成测试类方法、测试类,以及测试 module 的 准备操作;
使用 teardown_method、teardown_class、teardown_module 来分别完成测试类方法、测试类,以及测试 module 清理操作。
但是这种方式存在一个比较明显的缺陷。
例如,在同一个测试类中,存在多个测试方法,假设每一个测试方法需要不同的 setup 或者 teardown 函数,此时该怎么办呢?
又比如,setup 和 teardown 其实都属于测试夹具(Test Fixtures),如果我想把所有测试夹具全部放到一个函数中去管理,能做到吗?
pytest 考虑到了这种情况,并且提供了一个更加高级的功能,那就是 fixture 装饰器。
fixtures 可用作初始化测试服务、数据和状态,也常常用来在测试执行前或测试执行后进行测试的前置操作或后置操作。
fixtures 可作为共享数据使用,也可被其他函数、模块、类或者整个项目,甚至另外的 fixtures 调用。
1.fixtures 语法
pytest.fixtures 的语法如下:
fixture(scope="function", params=None, autouse=False, ids=None, name=None)从语法可以看到 fixture 的5个参数如下:
scope:用于控制 fixture 的作用范围
这个参数有以下4个级别:
function:在每一个 function 或者类方法中都会调用(默认)。
class:在每一个类中只调用一次。
module:每一个 .py 文件调用一次;该文件内可以有多个 function 和 class。
session:一个 session 调用一次。
params:一个可选的参数列表
params 以可选的参数列表形式存在。在测试函数中使用时,可通过 request.param 接收设置的返回值(即 params 列表里的值)。params 中有多少元素,在测试时,引用此 fixture 的函数就会调用几次。
autouse:是否自动执行设置的 fixtures
当 autouse 为 True 时,测试函数即使不调用 fixture 装饰器,定义的 fixture 函数也会被执行。
ids:指定每个字符串 id
当有多个 params 时,针对每一个 param,可以指定 id,这个 id 将变为测试用例名字的一部分。如果没有提供 id,则 id 将自动生成。
name:fixture 的名称
name 是 fixtures 的名称, 它默认是你装饰的那个 fixture 函数的名称。你可以通过 name 参数来更改这个 fixture 名称,更改后,如果这个 fixture 被调用,则使用你更改过的名称即可。
2.fixtures 用法
fixtures 有多种使用方式,举例说明如下。
(1)、通过 fixture 函数名直接使用
#test_fixture_usage.py
import pytest
# 首先, 在fixture函数上,加@pytest.fixture()
@pytest.fixture()
def my_method():
print('This is testing fixture')
# 其次,把fixture函数的函数名作为参数,传入被测试用例
def test_use_fixtures(my_method):
print('Please follow Testing from WL')通过 fixture 函数名使用 fixture 的步骤是:
在 fixture 函数上,加 @pytest.fixture(),上例中 my_method 这个方法将作为 fixture 使用;
把 fixture 函数的函数名作为参数,传入被测试用例。
注意:函数 test_use_fixtures 的入参必须是 my_method 这个方法名,跟 fixture 函数保持一致。
通过运行以上代码,在运行结果里,你会发现,my_method 即定义的 fixture 的方法先于测试函数的其他语句开始执行(相当于setup功能)。
(2)、通过 usefixtures 装饰器使用
通过把 fixture 作为测试函数入参的方式,可以达到为每一个测试函数配置不同的 setup和teardown 的功能,但这样会让 fixture 和我的测试函数耦合在一块,不利于测试函数的重用与测试框架的架构清晰。
因此 pytest 提供了 pytest.mark.usefixtures 这个装饰器。
以下代码举例说明了 usefixtures 的具体用法:
#test_fixture_usage.py
import pytest
@pytest.fixture()
def my_method():
print('This is Testing fixture')
# 函数直接使用fixture
@pytest.mark.usefixtures('my_method')
def test_use_fixtures():
print('Please follow Testing from WL')
class TestClass1:
# 类方法使用fixture
@pytest.mark.usefixtures('my_method')
def test_class_method_usage(self):
print('[classMethod]Please follow Testing from WL')
# 类直接使用fixture
@pytest.mark.usefixtures('my_method')
class TestClass2:
def test_method_usage_01(self):
pass
def test_method_usage_02(self):
pass由这段代码你可以看到,usefixtures 可以被函数、类方法,以及类调用。
(3)、fixture 多参数使用
上述使用方式实现了使不同的测试函数调用不同的测试 fixtures,那么如果我们 fixture 带参数该怎么办呢?请看如下代码:
import pytest
@pytest.fixture(params=['hello', 'Testing'])
def my_method(request):
return request.param
def test_use_fixtures_01(my_method):
print('this is the first test')
print(my_method)
@pytest.mark.usefixtures('my_method')
def test_use_fixtures_02():
print('this is the second test')
# 注意,如果在这里想通过print(my_mthod)来打印出fixuture提供的参数,是不行的, 因为使用usefixtures无法获取fixture的返回值,如需要fixture的返回值,则需用test_use_fixtures_01那样的调用方式执行这段代码,将会看到有4条测试用例被执行。由此可见,pytest 通过 fixture 和其参数 params 实现了数据驱动。
(4)、通过 autouse 参数隐式使用
以上方式实现了 fixtures 和测试函数的松耦合,但是仍然存在问题:每个测试函数都需要显式声明要用哪个 fixtures。
基于此,pytest 提供了autouse 参数,允许我们在不调用 fixture 装饰器的情况下使用定义的fixture,请看下面的例子:
#test_fixture_usage.py
import pytest
@pytest.fixture(params=['hello', 'Testing'], autouse=True, ids=['test1', 'test2'], name='test')
def my_method(request):
print(request.param)
def test_use_fixtures_01():
print('this is the first test')
def test_use_fixtures_02():
print('this is the second test')通过运行上段代码,并使用 allure[allure如何生成测试报告推文链接] 生成测试报告的结果如下:
当定义了 fixture 函数,并且 autouse 为 True 时,无须显式的在测试函数中声明要使用 fixture(在本例中,你看不到 my_method 这个 fixture 在测试方法中被显式调用)。定义的 fixture 将在 pytest.fixtures 指定的范围内,对其下的每一个测试函数都应用 fixture。
在本例中,scope 参数没有定义,将使用默认值“function”, 即每一个测试函数都会执行, 而我们的 params 又提供了两组参数,所以共 4 条测试用例被执行。
请注意下测试用例名称,针对每一个测试用例,因为在@pytest.fixture 指定了 ids 为 ['test1', 'test2'], 故测试用例名中也包括了指定的 id。
(5)、多 fixture 笛卡尔积使用
当你有多个 fixture 需要叠加使用时, 可以叠加使用。注意:此方式将把 fixure 的各组参数以笛卡尔积的形式组织,以下列代码为例,执行将生成 4 条测试用例。
import pytest
class TestClass:
@pytest.fixture(params=['hello', 'Testing'], autouse=True)
def my_method1(self, request):
print('the param are:{}'.format(request.param))
return request.param
@pytest.fixture(params=['world', 'is good'], autouse=True)
def my_method2(self, request):
print('the param are:{}'.format(request.param))
return request.param
def test_use_fixtures_01(self):
pass
(6)、使用 conftest.py 来共享 fixture
通过上面的举例学习,大家应该掌握了如何在同一个文件中进行 fixture 的定义、共享和使用。但在日常工作测试中,我们常常需要在全局范围内使用同一个测试前置操作。
例如:测试开始时首先进行登录操作,接着连接数据库等操作。
这种情况下,我们就需要使用 conftest.py。在 conftest.py 中定义的 fixture 不需要进行 import,pytest 会自动查找使用。pytest 查找 fixture 的顺序是首先查找测试类(Class),接着查找测试模块(Module),然后是 conftest.py 文件,最后是内置或者第三方插件。
下面来看下如何使用 conftest.py
假设目录结构如下:
|--APITest
|--tests
|--test_fixture1.py
|--test_baidu_fixture_sample.py
|--conftest.py
|--__init__.pyconftest.py 的代码如下:
# conftest.py
import pytest
import requests
from selenium import webdriver
@pytest.fixture(scope="session")
# 此方法名可以是你登录的业务代码,也可以是其他,这里暂命名为login
def login():
driver = webdriver.Chrome()
driver.implicitly_wait(30)
base_url = "http://www.baidu.com/"
s = requests.Session()
yield driver, s, base_url
print('turn off browser driver')
driver.quit()
print('turn off requests driver')
s.close()
@pytest.fixture(scope="function", autouse=True)
def connect_db():
print('connecting db')
# 此处写你的连接db的业务逻辑
pass
test_fixture1.py 的代码如下:
# test_fixture1.py
import pytest
class TestClass:
def test_use_fixtures_01(self, login):
print('I am data:{}'.format(login))
test_baidu_fixture_sample.py 的代码如下:
import time
import pytest
@pytest.mark.baidu
class TestBaidu:
@pytest.mark.parametrize('search_string, expect_string', [('Testing', 'Testing'), ('helloworld.com', 'Testing')])
def test_baidu_search(self, login, search_string, expect_string):
driver, s, base_url = login
driver.get(base_url + "/")
driver.find_element_by_id("kw").send_keys(search_string)
driver.find_element_by_id("su").click()
time.sleep(2)
search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')
print(search_results)
assert (expect_string in search_results) is True
if __name__ == "__main__":
pytest.main([])在命令行中通过如下代码执行:
D:\Auto\APITest>pytest -s -q --tb=no tests --alluredir=./allure_reports
测试执行完成后,查看执行结果:
从上图中可以注意到,connecting db 这条语句被打印了三次,是因为在 conftest.py 里把 connect_db 这个 fixture 的 scope 设置为 function 且 autouse 的属性值是 True。而 turn off browser driver,turn off requests driver 这两条语句仅仅执行了一次,是因为 login 这个 fixture 的 scope 是 session,故它在整个 session 中仅仅执行了一次。
另外请注意下在 fixture login 中,有如下的语句:
...
...
yield driver, s, base_url
print('turn off browser driver')
driver.quit()
print('turn off requests driver')
s.close()这个是什么意思呢?在 pytest 的 fixture 里,yield关键字语句之前的属于 set up,而 yield 以后的语句属于 tear down。
这样你就明白了,为什么以下语句是最后执行的了:
print('turn off browser driver')
driver.quit()
print('turn off requests driver')
s.close()pytest.mark.parametrize 和 pytest.fixture 结合使用
通过上面的讲解我们了解到,在 pytest 中可以使用 pytest.mark.parametrize 装饰器进行数据驱动测试,可以使用 pytest.fixture 装饰器进行测试的 setup、teardown,以及 fixture 共享的测试。
当 pytest.mark.parametrize 和 pytest.fixture 结合起来,能起到什么效果呢?
(1)、减少了重复代码,实现了代码全局共享
所有的测试前置及后置功能均可以定义在 conftest.py 文件中,供整个测试使用,而不必在每一个测试类中定义。这样做大大减少了重复代码,且 conftest.py 定义在项目根目录,就可以应用在全局,定义在某一个文件夹,就可以应用于这个文件夹下的所有测试文件。
(2)、可以使测试仅关注测试自身
测试仅围绕自身业务进行编码即可,配合使用 conftest.py 及 pytest.fixture 可实现,在一个测试类中,仅仅包括测试自身的代码,而不必考虑测试前的准备以及测试后的清理工作。
(3)、框架迁移更容易
如果是 UI 自动化测试,可在 conftest.py 文件中包括 Web Driver 的所有操作,如果是 API 测试,可在 conftest.py 文件中编写所有接口请求操作。这样当新项目需要应用自动化框架时,仅需更改 tests 文件夹下的测试用例即可。
pytest.mark.parametrize 和 pytest.fixture 结合示例:
# test_sample.py
import pytest
@pytest.fixture()
def is_odd(request):
print('Now the parameter are:--{}\n'.format(request.param))
if int(request.param) % 2 == 0:
return False
else:
return True
@pytest.mark.parametrize("is_odd", [1, 0], indirect=True)
def test_is_odd(is_odd):
if is_odd:
print("is odd number")
else:
print("is not odd number")
if __name__ == "__main__":
pytest.main([])上述代码定义了一个 fixture 方法 is_odd 和一个数据驱动的方法 test_is_odd。其中,fixture 方法 is_odd 判断一个数是否是奇数;而数据驱动的方法 test_is_odd 会提供一组数据,并且调用 is_odd 这个 fixture 进行判断。
总结
今天的分享内容是 pytest 测试框架如何进行数据驱动,可以通过结合使用 pytest.mark.parametrize 和 pytest.fixture 装饰器。如果你学会了 pytest.mark.parametrize 和 pytest.fixture 的各种用法,对你的测试框架将可以运用自如。
相关推荐
- 安全教育登录入口平台(安全教育登录入口平台官网)
-
122交通安全教育怎么登录:122交通网的注册方法是首先登录网址http://www.122.cn/,接着打开网页后,点击右上角的“个人登录”;其次进入邮箱注册,然后进入到注册页面,输入相关信息即可完...
- 大鱼吃小鱼经典版(大鱼吃小鱼经典版(经典版)官方版)
-
大鱼吃小鱼小鱼吃虾是于谦跟郭麒麟的《我的棒儿呢?》郭德纲说于思洋郭麒麟作诗的相声,最后郭麒麟做了一首,师傅躺在师母身上大鱼吃小鱼小鱼吃虾虾吃水水落石出师傅压师娘师娘压床床压地地动山摇。...
-
- 哪个软件可以免费pdf转ppt(免费的pdf转ppt软件哪个好)
-
要想将ppt免费转换为pdf的话,我们建议大家可以下一个那个wps,如果你是会员的话,可以注册为会员,这样的话,在wps里面的话,就可以免费将ppt呢转换为pdfpdf之后呢,我们就可以直接使用,不需要去直接不需要去另外保存,为什么格式转...
-
2026-02-04 09:03 off999
- 电信宽带测速官网入口(电信宽带测速官网入口app)
-
这个网站看看http://www.swok.cn/pcindex.jsp1.登录中国电信网上营业厅,宽带光纤,贴心服务,宽带测速2.下载第三方软件,如360等。进行在线测速进行宽带测速时,尽...
- 植物大战僵尸95版手机下载(植物大战僵尸95 版下载)
-
1可以在应用商店或者游戏平台上下载植物大战僵尸95版手机游戏。2下载教程:打开应用商店或者游戏平台,搜索“植物大战僵尸95版”,找到游戏后点击下载按钮,等待下载完成即可安装并开始游戏。3注意:确...
- 免费下载ppt成品的网站(ppt成品免费下载的网站有哪些)
-
1、Chuangkit(chuangkit.com)直达地址:chuangkit.com2、Woodo幻灯片(woodo.cn)直达链接:woodo.cn3、OfficePlus(officeplu...
- 2025世界杯赛程表(2025世界杯在哪个国家)
-
2022年卡塔尔世界杯赛程公布,全部比赛在卡塔尔境内8座球场举行,2022年,决赛阶段球队全部确定。揭幕战于当地时间11月20日19时进行,由东道主卡塔尔对阵厄瓜多尔,决赛于当地时间12月18日...
- 下载搜狐视频电视剧(搜狐电视剧下载安装)
-
搜狐视频APP下载好的视频想要导出到手机相册里方法如下1、打开手机搜狐视频软件,进入搜狐视频后我们点击右上角的“查找”,找到自已喜欢的视频。2、在“浏览器页面搜索”窗口中,输入要下载的视频的名称,然后...
- 永久免费听歌网站(丫丫音乐网)
-
可以到《我爱音乐网》《好听音乐网》《一听音乐网》《YYMP3音乐网》还可以到《九天音乐网》永久免费听歌软件有酷狗音乐和天猫精灵,以前要跳舞经常要下载舞曲,我从QQ上找不到舞曲下载就从酷狗音乐上找,大多...
- 音乐格式转换mp3软件(音乐格式转换器免费版)
-
有两种方法:方法一在手机上操作:1、进入手机中的文件管理。2、在其中选择“音乐”,将显示出手机中的全部音乐。3、点击“全选”,选中所有音乐文件。4、点击屏幕右下方的省略号图标,在弹出菜单中选择“...
- 电子书txt下载(免费的最全的小说阅读器)
-
1.Z-library里面收录了近千万本电子书籍,需求量大。2.苦瓜书盘没有广告,不需要账号注册,使用起来非常简单,直接搜索预览下载即可。3.鸠摩搜书整体风格简洁清晰,书籍资源丰富。4.亚马逊图书书籍...
- 最好免费观看高清电影(播放免费的最好看的电影)
-
在目前的网上选择中,IMDb(互联网电影数据库)被认为是最全的电影网站之一。这个网站提供了各种类型的电影和电视节目的海量信息,包括剧情介绍、演员表、评价、评论等。其还提供了有关电影制作背后的详细信息,...
- 孤单枪手2简体中文版(孤单枪手2简体中文版官方下载)
-
要将《孤胆枪手2》游戏的征兵秘籍切换为中文,您可以按照以下步骤进行操作:首先,打开游戏设置选项,通常可以在游戏主菜单或游戏内部找到。然后,寻找语言选项或界面选项,点击进入。在语言选项中,选择中文作为游...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
win7系统还原步骤图解(win7还原电脑系统的步骤)
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
16949认证费用是多少(16949审核员太难考了)
-
linux软件(linux软件图标)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
windows7旗舰版多少钱(win7旗舰版要多少钱)
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
