pytest简易教程(36):pytest + allure最佳实践
pytest简易教程汇总,详见:https://www.cnblogs.com/uncleyong/p/17982846
简介
上一篇我们已经生成了allure报告,但是可读性不是很强,
所以我们还需要对报告优化,也就是通过allure提供的装饰器添或者代码方式加一些描述信息。
官方示例:https://github.com/allure-examples/allure-examples
常用装饰器
说明:
- feature和story类似于父子关系
- 如果不加 @allure.feature、@allure.story,在Behaviors下测试用例就不会分类显示
- 如果没有添加 @allure.title() ,测试用例的标题默认就是函数名
添加方式及常用格式
方式一:装饰器
放在测试用例上,也就是测试方法上
1 2 3 4 5 6 | @allure .epic( "项目" ) @allure .feature( "模块" ) @allure .story( "子模块" ) @allure .title( "标题" ) @allure .severity(allure.severity_level.CRITICAL) @allure .description( "描述" ) |
优点:可以指定feature、story、severity运行,--allure-features="xxx" --alure-stories="xxx","yyy" --allure-severities blocker,critical,normal
不足:每个用例分别添加描述信息比较繁琐
补充:severity_level的值是枚举
方式二:代码(推荐)
放在测试用例下,也就是测试方法下
1 2 3 4 5 6 | allure.dynamic.epic( "项目" ) allure.dynamic.feature( "模块" ) allure.dynamic.story( "子模块" ) allure.dynamic.title( "标题" ) allure.dynamic.severity( "优先级" ) allure.dynamic.description( "描述" ) |
优点:描述信息实现参数化
不足:不能指定feature、story、severity运行
实际工作中,如果用到参数化,描述信息可以放到参数化数据中,这样就可以用方式二读取测试数据动态生成描述信息。
报告添加severity(装饰器方式)
应用场景:想要根据优先级执行用例,可以通过allure.severity来添加标记
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : 韧 # @wx :ren168632201 # @Blog :https://www.cnblogs.com/uncleyong/ import allure # 没有指定,默认是normal @allure .feature( "模块1" ) @allure .story( "m1子模块1" ) def test_no_severity(): pass @allure .feature( "模块2" ) @allure .story( "m2子模块1" ) @allure .severity(allure.severity_level.TRIVIAL) def test_trivial_severity(): pass @allure .feature( "模块3" ) @allure .story( "m3子模块1" ) @allure .severity(allure.severity_level.NORMAL) def test_normal_severity(): pass @allure .feature( "模块4" ) @allure .story( "m4子模块1" ) @allure .severity(allure.severity_level.NORMAL) class TestCase: def test_inclass_normal_severity( self ): pass # 优先级就近原则 @allure .severity(allure.severity_level.CRITICAL) def test_inclass_critical_severity( self ): pass |
指定单个优先级执行
指定多个优先级执行
指定单个模块执行
指定模块和优先级执行,结果是并集,不是交集
同时指定模块和子模块执行,结果是并集,不是交集
pytest case\test_qzcsbj6.py -vs --alluredir=./result --clean-alluredir --allure-features=模块4 --allure-stories=m3子模块1
报告添加epic、feature、story、title、severity、description(代码方式)
应用场景:在Behaviors下测试用例分类显示,在用例详情中显示severity和description
比如接口自动化中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : 韧 # @wx :ren168632201 # @Blog :https://www.cnblogs.com/uncleyong/ import allure import pytest data = [ { "epic" : "全栈测试笔记" , "feature" : "用户模块" , "story" : "用户注册" , "title" : "用户注册成功" , "description" : "desc - 用户注册成功" , "severity" : "critical" , "request" :{ "url" : "/qzcsbj/user/register" , "method" : "post" , "headers" : { 'Content-Type' : 'application/json' }, "params" :{ "uname" : "qzcsbj" , "pwd" : "123456" } } }, { "epic" : "全栈测试笔记" , "feature" : "用户模块" , "story" : "用户注册" , "title" : "用户注册失败" , "description" : "desc - 用户注册失败" , "severity" : "critical" , "request" : { "url" : "/qzcsbj/user/register" , "method" : "post" , "headers" : { 'Content-Type' : 'application/json' }, "params" : { "uname" : "ren" , "pwd" : "666666" } } }, { "epic" : "全栈测试笔记" , "feature" : "订单模块" , "story" : "查询订单" , "title" : "查询订单成功" , "description" : "desc - 查询订单成功" , "severity" : "normal" , "request" : { "url" : "/qzcsbj/order/search" , "method" : "post" , "headers" : { 'Content-Type' : 'application/json' }, "params" : { "product" : "thinkpad" } } } ] @pytest .mark.parametrize( 'casedata' ,data) class TestCase: def test_case( self , casedata): allure.dynamic.epic(casedata[ "epic" ]) allure.dynamic.feature(casedata[ "feature" ]) allure.dynamic.story(casedata[ "story" ]) allure.dynamic.title(casedata[ "title" ]) allure.dynamic.severity(casedata[ "severity" ]) allure.dynamic.description(casedata[ "description" ]) print (f "---测试数据是:{casedata['request']}" ) assert 1 = = 1 |
运行结果:
生成测试报告
打开报告
其它参考:有动态allure描述和无动态allure描述(https://www.cnblogs.com/uncleyong/p/18050265)
报告添加step
应用场景:ui自动化测试中,测试过程中的每个步骤可以通过step来描述,通过with allure.step():放在测试用例方法里面,测试步骤的代码需要被该语句包含
如果中间某个步骤失败,后面步骤不会执行
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : 韧 # @wx :ren168632201 # @Blog :https://www.cnblogs.com/uncleyong/ import allure @allure .epic( "全栈测试笔记" ) @allure .feature( "用户模块" ) @allure .story( "注册" ) class TestRigister: @allure .title( "注册成功" ) @allure .severity( "critical" ) @allure .description( "desc - 注册成功" ) def test_reg_success( self ): with allure.step( "步骤1:打开注册页面" ): print ( "打开注册页面" ) with allure.step( "步骤2:输入注册信息" ): print ( "输入注册信息" ) with allure.step( "步骤3:提交注册,注册成功" ): print ( "提交注册,注册成功" ) assert 1 = = 1 @allure .title( "注册失败" ) @allure .severity( "critical" ) @allure .description( "desc - 注册失败" ) def test_reg_fail( self ): with allure.step( "步骤1:打开注册页面" ): print ( "打开注册页面" ) with allure.step( "步骤2:输入注册信息" ): print ( "输入注册信息" ) with allure.step( "步骤3:提交注册,注册失败" ): print ( "提交注册,注册失败" ) assert 1 = = 2 |
执行:
生成最终报告:
打开报告:
右侧多了Test body
报告添加link、issue、testcase
应用场景:
源码:本质上,调用的都是link
1 2 3 4 5 6 7 8 9 10 | def link(url, link_type = LinkType.LINK, name = None ): return safely(plugin_manager.hook.decorate_as_link(url = url, link_type = link_type, name = name)) def issue(url, name = None ): return link(url, link_type = LinkType.ISSUE, name = name) def testcase(url, name = None ): return link(url, link_type = LinkType.TEST_CASE, name = name) |
指定link的参数
Link_Type对应源码LinkType,是枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : 韧 # @wx :ren168632201 # @Blog :https://www.cnblogs.com/uncleyong/ import allure TEST_CASE_LINK = 'https://www.cnblogs.com/uncleyong/' @allure .link( "https://www.cnblogs.com/uncleyong/" ) def test_link(): pass @allure .link( "https://www.cnblogs.com/uncleyong/" , name = "全栈测试笔记" ) def test_named_link(): pass @allure .issue( "16" , "bug地址" ) def test_testcase_link(): pass @allure .testcase(TEST_CASE_LINK, "测试用例管理平台地址" ) def test_issue_link(): pass |
运行:这里LinkType的值是issue,表示bug地址
pytest case\test_qzcsbj6.py -s -q --alluredir=./result --clean-alluredir --allure-link
-pattern=issue:https://www.cnblogs.com/uncleyong/{}
跳转的地址
优先级默认是normal
报告添加attach
源码:
allure.attach(),用于传文本
- body:文本内容
- name:展示名称
- attachment_type:类型
- extension:扩展名
allure.attach.file(),用于传附件
- source:附件源路径
- name:展示名称
- attachment_type:类型
- extension:扩展名
支持的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class AttachmentType(Enum): def __init__( self , mime_type, extension): self .mime_type = mime_type self .extension = extension TEXT = ( "text/plain" , "txt" ) CSV = ( "text/csv" , "csv" ) TSV = ( "text/tab-separated-values" , "tsv" ) URI_LIST = ( "text/uri-list" , "uri" ) HTML = ( "text/html" , "html" ) XML = ( "application/xml" , "xml" ) JSON = ( "application/json" , "json" ) YAML = ( "application/yaml" , "yaml" ) PCAP = ( "application/vnd.tcpdump.pcap" , "pcap" ) PNG = ( "image/png" , "png" ) JPG = ( "image/jpg" , "jpg" ) SVG = ( "image/svg-xml" , "svg" ) GIF = ( "image/gif" , "gif" ) BMP = ( "image/bmp" , "bmp" ) TIFF = ( "image/tiff" , "tiff" ) MP4 = ( "video/mp4" , "mp4" ) OGG = ( "video/ogg" , "ogg" ) WEBM = ( "video/webm" , "webm" ) PDF = ( "application/pdf" , "pdf" ) |
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : 韧 # @wx :ren168632201 # @Blog :https://www.cnblogs.com/uncleyong/ import allure @allure .epic( "全栈测试笔记" ) @allure .feature( "用户模块" ) @allure .story( "注册" ) class TestRigister: # @allure.title("注册成功") # @allure.severity("critical") # @allure.description("desc - 注册成功") # def test_reg_success(self): # with allure.step("步骤1:打开注册页面"): # print("打开注册页面") # with allure.step("步骤2:输入注册信息"): # print("输入注册信息") # with allure.step("步骤3:提交注册,注册成功"): # print("提交注册,注册成功") # assert 1 == 1 @allure .title( "注册失败" ) @allure .severity( "critical" ) @allure .description( "desc - 注册失败" ) def test_reg_fail( self ): with allure.step( "步骤1:打开注册页面" ): print ( "打开注册页面" ) allure.attach( "文本信息" , name = "文本" , attachment_type = allure.attachment_type.TEXT) with allure.step( "步骤2:输入注册信息" ): print ( "输入注册信息" ) allure.attach( '<div class="wx"><img src="https://files-cdn.cnblogs.com/files/uncleyong/wx.bmp" alt="微信:ren168632201"></div>' , name = "html" , attachment_type = allure.attachment_type.HTML) with allure.step( "步骤3:提交注册,注册失败" ): print ( "提交注册,注册失败" ) allure.attach. file ( "D:\wx.png" , name = "这是图片" , attachment_type = allure.attachment_type.JPG, extension = "jpg" ) assert 1 = = 1 |
图片在D盘
运行
生成并打开报告
结果:右侧步骤可以看到添加的文本、html、图片
思考题
动态添加描述信息,比如feature、story等,
1 2 | allure.dynamic.feature( "模块" ) allure.dynamic.story( "子模块" ) |
如果实现指定feature、story运行测试用例?
比如:
pytest -s -q --alluredir=./result --clean-alluredir --allure-features=用户模块 --allure-stories=登录
pytest -s -q --alluredir=./result --clean-alluredir --allure-stories=登录
pytest -s -q --alluredir=./result --clean-alluredir --allure-severities=normal,critical
> > > > > > 下一系列:自动化测试框架开发(python + pytest + requests + allure + jsonpath + pyyaml)
__EOF__

关于博主:擅长性能、全链路、自动化、企业级自动化持续集成(DevTestOps)、测开等
面试必备:项目实战(性能、自动化)、简历笔试,https://www.cnblogs.com/uncleyong/p/15777706.html
测试提升:从测试小白到高级测试修炼之路,https://www.cnblogs.com/uncleyong/p/10530261.html
欢迎分享:如果您觉得文章对您有帮助,欢迎转载、分享,也可以点击文章右下角【推荐】一下!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)