Pytest7基础
一、前言
Pytest基于Python设计,是一款开源免费、功能全面、性能可靠、报告优美的单元测试框架。能够支持简单的单元测试和复杂的功能测试,还可以用来做selenium/appnium
等自动化测试、接口自动化测试(pytest+requests)
。现今也被汽车OEM广泛使用。
二、工具说明
Pycharm 2023 pytest 7.4.3
三、创建工程
创建工程时重点要求使用VituralEnvironment
,这里直接选择。
四、Pytest环境安装
在根目录下创建文件Requirements.txt
文件,内容为:
pytest pytest-html pytest-xdist pytest-ordering pytest-rerunfailures allure-pytest
随后在Pycharm的命令行窗口输入运行:
pip install -r Requirements.txt
安装完毕后继续命令行输入查看是否正确:
pytest --version
查看到版本号后即可正确使用,解释下刚才安装的包:
pytest pytest环境 pytest-html 用于生成pytest-html格式报告 pytest-xdist 用于用例的分布式执行 pytest-ordering 用于用例的顺序调整 pytest-rerunfailures 用于失败重跑 allure-pytest 用于生成allure报告
五、Pytest框架
5.1 框架示意
在Pytest中表现为:
5.2 配置文件
Requirements.txt
为环境安装文件,前面小节已经提到
pytest.ini
文件为参数配置文件,具体见下一节。
conftest.py
文件为装饰器文件,具体见6.3.4.3小节,不是最必要的文件。
5.3 主文件
测试入口,使用pytes.mian()
开始测试,
import pytest #执行本文件中的所有测试类,一个main会自动执行所有的py文件 if __name__ == '__main__': #基于pytest.ini运行 pytest.main()
5.4 测试模块
定义编写各测试用例,例如:
import pytest #测试类 class TestEcuSleepWakeup: #测试用例/测试接口/测试函数 def test_01_sleep(self,test_fixture): print("这是一个测试ECU休眠用例!") @pytest.mark.useragemode #装饰器 def test_02_wakeup(self,test_fixture): print("这是一个测试ECU唤醒用例!")
六、Pytest语法基础
6.1 命名规则
1.模块名必须以test_开头或者_test结尾 2.测试类必须以Test开头,不能有int方法 3.测试用例必须以test开头
6.2 运行方式
6.2.1 运行模式
1.主函数模式: (1)全局运行:pytest.main(['-vs']) (2)指定模块:pytest.main(['-vs','test_login.py']) (3)指定目录:pytest.main(['-vs','../test_interface']) (4)指定函数:pytest.main(['-vs','../test_interface/test_interface_01.py::test_02_TestcaseInterface']) 2.命令行模式: (1)切换到测试用例的目录:cd testcase (3)运行:pytest -vs test_login.py 3.读取ini文件运行:pytest.ini (1)ini文件,一般放在项目的根目录中 (2)ANSI格式,可以Notepad修改,pytest 7.4.3 支持UTF8编码 (3)将会同步修改主函数模式和命令函模式 (4)分组执行:冒烟测试,分模块执行,分接口和web执行 单选:pytest.main(['-vs','test_login.py','-m=somke']) 多选:pytest.main(['-vs','-m=smoke or useragemode'])
6.2.2 基于读取Pytest.ini文件运行
主要解释下第三种读取INI文件运行,需要在根目录下创建一个pytest.ini
文件:
[pytest] #命令行 addopts = -vs -m 'smoke or useragemode' --html ./report/report.html #测试路径 ;testpaths = ./testcase #测试文件 python_files = test_*.py #测试类名 python_classes = Test* #测试函数名 python_functions = test_* #分组名 markers = smoke:somking test useragemode:usage mode
pytest.ini
与主函数pytest.main()
会有多个参数冲突,比如文件名、路径等,切记不可冲突!
6.3 命令语法基础
6.3.1 命令行基础命令
在pytest.ini - addopts
或者Pycharm的命令行
运行pytest时,可以有以下几种参数:
-s 打印所有的调试信息 -v 打印用例名及测试结果 -vs 组合使用 -x 测试失败时停止测试 -k='string' 根据用例的部分字符串指定测试用例 -m='string' 根据ini文件定义的测试分组执行用例 pytest.main(['-vs','test_login.py','-m=somke']) --maxfail=num 最大用例失败数 -n=num 多线程/分布式运行 pytest.main(['-vs','test_login.py','-n=2']) --reruns=num 失败重跑num次 pytest.main(['-vs','test_login.py','--reruns=2'])
例如我们已经给出的:
#命令行 addopts = -vs -m 'smoke or useragemode' --html ./report/report.html
6.3.2 分组执行
在pytest.ini文件中,我们定义了分组名Smoke
与useragemode
,因此测试函数前只需要加上相关标签即可。
import pytest #测试类 class TestEcuSleepWakeup: #测试用例/测试接口/测试函数 def test_01_sleep(self,test_fixture): print("这是一个测试ECU休眠用例!") @pytest.mark.useragemode #分组 def test_02_wakeup(self,test_fixture): print("这是一个测试ECU唤醒用例!")
在pytest.ini的命令行添加即可选择分组运行:
#命令行 addopts =-m 'smoke or useragemode'
6.3.3 顺序执行及跳过
可以在用例前添加装饰器来执行当前测试类中的用例执行顺序:
@pytest.mark.run(order=1) #执行顺序:1为执行顺序,可以是3,可以时2 @pytest.mark.skip(age<=18,reason = '年龄跳过') #有条件跳过:小于18岁跳过;可以删除条件
import pytest import time import setuptools #测试类 class TestLogin: #测试用例/测试接口/测试函数 @pytest.mark.run(order=2) #装饰器:第二个执行 def test_login_01_Print(self): time.sleep(3) print("这是一个TestLogin用例!") assert 1==2 #断言 # @pytest.mark.skip(reason = '不想执行') @pytest.mark.run(order=1) #装饰器:第一个执行 def test_login_02_Print(self): time.sleep(3) print("这是一个TestLogin用例!")
6.3.4 固定夹具及装饰器
6.3.4.1 固定夹具
setup_method() 相当于prepare,作用于添加的测试类中每一个用例 teardown_method() 相当于finalize,作用于添加的测试类中每一个用例 setup_class() 作用于添加的测试类整体,只执行一次初始化工作 setup_teardown() 作用于添加的测试类整体,只执行一次扫尾动作
分组执行是pytest用例执行的核心,下面的测试模块文件我们结合了上面讲到的一些命令及装饰器用法,请仔细理解:
import pytest import time import setuptools #测试类 class TestLogin: def setup_class(self): print("\n给类做一些前置工作"); #夹具 def setup_method(self): print("\n给用例做一些前置工作"); #测试用例/测试接口/测试函数 @pytest.mark.run(order=2) #装饰器:第二个执行 @pytest.mark.smoke #装饰器:自定义测试分组,比如冒烟测试 def test_login_01_Print(self): time.sleep(3) print("这是一个TestLogin用例!") assert 1==2 #断言 # @pytest.mark.skip(reason = '不想执行') @pytest.mark.run(order=1) #装饰器:第一个执行 @pytest.mark.useragemode def test_login_02_Print(self): time.sleep(3) print("这是一个TestLogin用例!") def teardown_method(self): print("\n给用例一些结束动作") def teardown_class(self): print("\n给类做一些结束工作");
6.3.4.2 装饰器Fixture
装饰器Fixture的做用类同测试夹具,都是来给测试用例做一些前置或者扫尾动作。注意,不设置的形参请删除,避免skipped
。
@pytest.fixture(scope="",params="",autouse="",ids="",name="") #scope 表示fixture的方法作用域。默认是function、还有class、moudle、package #params 参数化,支持列表,字典,元组,字典列表,字典元组 #autouse 自动执行,默认false,请谨慎使用! #ids 参数化时,当使用params时,给每一个值设置一个变量名, #name 被装饰器标志的方法起个别名
scope:标明装饰器的执行范围,默认function
,有module、class、function
三种可选。
import pytest @pytest.fixture(scope="function") def test_fixture(): print("\nfixture:这是前后置的方法,可以实现部分或局部的前置动作") yield print("\nfixture:这是前后置的方法,可以实现部分或局部的后置动作") #测试类 class TestEcuSleepWakeup: #测试用例/测试接口/测试函数 def test_01_sleep(self,test_fixture): print("这是一个测试ECU休眠用例!") @pytest.mark.useragemode #装饰器 def test_02_wakeup(self,test_fixture): print("这是一个测试ECU唤醒用例!")
params:给用例传入参数,可以自动根据参数的数量来循环遍历当前函数。
import pytest @pytest.fixture(scope="function",params=["apple",'pear']) def test_fixture(): print("\nfixture:这是前后置的方法,可以实现部分或局部的前置动作") yield print("\nfixture:这是前后置的方法,可以实现部分或局部的后置动作") #测试类 class TestEcuSleepWakeup: #测试用例/测试接口/测试函数 def test_01_sleep(self,test_fixture): print("这是一个测试ECU休眠用例!") @pytest.mark.useragemode #装饰器 def test_02_wakeup(self,test_fixture): print("这是一个测试ECU唤醒用例!") print(str(test_fixture));
执行了两遍。但是可以看到传入的值为空,这是因为值需要被请求才可以,修改fixture
,注意yield
不能和return
同时使用,yield
也是返回,下面是两种用法:
@pytest.fixture(scope="function",params=["apple",'pear']) def test_fixture(request): #固定写法!下方结果示例的方法。 return request.param
@pytest.fixture(scope="function",params=["apple",'pear'],ids=['APPLE','PEAR']) def test_fixture(request): print("\nfixture:这是前后置的方法,可以实现部分或局部的前置动作") yield request.param print("\nfixture:这是前后置的方法,可以实现部分或局部的后置动作")
ids:与params组合使用,给每一个值一个名字。用处不大。
@pytest.fixture(scope="function",params=["apple",'pear'],ids=['APPLE','PEAR']) def test_fixture(request): return request.param
autouse:默认false
,如果是ture
,则不管当前.py文件中各测试类或者测试函数有无传入装饰器名testfixture
,都会执行装饰器,因此慎用!
name:用例别名,用于给def
的函数名重名,用处不大。
import pytest @pytest.fixture(scope="function",params=["apple",'pear'],ids=['APPLE','PEAR'],name="newfixture_name") def test_fixture(request): print("\nfixture:这是前后置的方法,可以实现部分或局部的前置动作") yield request.param print("\nfixture:这是前后置的方法,可以实现部分或局部的后置动作") #测试类 class TestEcuSleepWakeup: def test_01_sleep(self,newfixture_name): #必须使用别名!!! print("这是一个测试ECU休眠用例!") @pytest.mark.useragemode #装饰器 def test_02_wakeup(self,newfixture_name): #必须使用别名!!! print("这是一个测试ECU唤醒用例!") print(str(test_fixture));
大型工程中使用conftest.py与@Fixture结合来实现全局的前置配置。Fixture的进阶使用可以浏览知乎博主的文章【pytest】(三)pytest中的fixture - 知乎 (zhihu.com)
6.3.4.3 断言Assert与夹具配置
在6.3.4.2节中,我们介绍了装饰器Fixture
,使用方法时在每一个测试类.py
文件中编写装饰器。但是当多个py文件使用同一各装饰器时,为了避免重复定义,可以使用conftest.py
文件进行管理。例如:
#conftest.py文件 import pytest @pytest.fixture(scope="function",params=["apple",'pear'],ids=['APPLE','PEAR']) def test_fixture(request): print("\nfixture:这是前后置的方法,可以实现部分或局部的前置动作") yield request.param print("\nfixture:这是前后置的方法,可以实现部分或局部的后置动作") @pytest.fixture(scope="function") def test_fixture2(request): print("\nfixture2:这是前后置的方法,可以实现部分或局部的前置动作") yield request.param print("\nfixture2:这是前后置的方法,可以实现部分或局部的后置动作")
#test_sleep_wakeup.py文件 import pytest #测试类 class TestEcuSleepWakeup: #测试用例/测试接口/测试函数 def test_01_sleep(self,test_fixture): #没有定义分组,不会执行 print("这是一个测试ECU休眠用例!") @pytest.mark.useragemode #装饰器 def test_02_wakeup(self,test_fixture,test_fixture2): #可以使用多个装饰器,在前的装饰器会最先执行,最后结束 print("这是一个测试ECU唤醒用例!") print(str(test_fixture));
注意:
1.conftest.py
文件名称固定写法,不可更改。
2.conftest.py
文件最好在每一个测试模块的文件中都定义一个;也可以定义在根目录中。
3.contest.py
文件无须被测试类.py
文件import
,会自动识别。
4.使用多个装饰器时,注意装饰器的传入顺序。
6.3.5 Parametize参数化
在fixture
中可以参数化来实现用例的遍历,除此之外,我们可以直接给用例传入参数。
@pytest.mark.parametrize(args_name,args_value) args_name 参数名 args_value 参数值(列表、元组、字典列表、字典元组)
6.3.5.1 单参数用法
@pytest.mark.parametrize('args',['dog','cat']) def test_03_parameter(self,args): print("数据驱动:{%s}" %args)
如果加上6.3.4.3章节中定义的装饰器的两个参数,一共会执行四次:
@pytest.mark.parametrize('args',['dog','cat']) def test_03_parameter(self,args,test_fixture): print("数据驱动:{%s}" %args)
6.3.5.2 多参数用法
在上一小节中,只是传递单个字符,如果我们传入的是两层的列表成员,就需要在参数定义时规定两个参数:
@pytest.mark.parametrize('kind,name',[['animal1','dog'],['animal2','cat']]) def test_03_parameter(self,kind,name): #定义两个参数,名称与上面相同 print("数据驱动:{%s %s}" %(kind,name))
6.3.6 断言Assert
判断用例通过还是失败。
assert 1==2 #断言
6.3.7 Pytest-Allure报告
6.3.7.1 基础报告xml
在6.2.2中的pytest.ini文件中我们有一句命令行参数:
[pytest] #命令行 addopts = -vs -m 'smoke or useragemode' --html ./report/report.html
运行后可以得到一份简单的测试报告,此报告为pytest-html
插件生成:
但是这个报告的界面与可读性并不是非常好,于是引入allure报告(Requirements.txt
已安装)。
6.3.7.2 Allure报告
环境安装
1.allure测试报告为allure-pytest
插件生成。首先我们需要下载allure安装包(Releases · allure-framework/allure2 (github.com))并解压。
2.双击allure.bat运行批处理文件进行安装。
3.安装java(Windows下安装Allure步骤及注意事项 - 知乎 (zhihu.com))
4.安装完毕后将Bin文件夹的路径配置到系统变量中,随后重启电脑:
5.打开pycharm,检验安装:
报告生成
修改mian.py
文件,生成Allure正式报告
import pytest import os #执行本文件中的所有测试类,一个main会执行所有的py文件 if __name__ == '__main__': #基于pytest.ini运行 pytest.main(['--alluredir=./report/temp_jsonreport']) os.system('allure generate ./report/temp_jsonreport -o ./report/html --clean')
pytest.main命令解释 --alluredir=./report/temp_jsonreport #输出的json文件路径 os.system命令解释 allure generate ./report/temp_jsonreport #输出的allure报告的数据来源,来源于--alluredir生成的json临时报告 -o ./report/html --clean' #输出的.html报告,此为allure报告,其中--clean为清楚上一次生成的数据,注意此文件路径不可与--alluredir一样!
请在pycharm中打开,因为直接在文件夹中打开不会渲染输出!
本文来自博客园,作者:{张一默},转载请注明原文链接:https://www.cnblogs.com/YiMo9929/p/17798127.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)