HttpRunner3.X - 全面讲解如何落地项目实战
一、前言
接触httprunner框架有一段时间了,也一直探索如何更好的落地到项目上,本篇主要讲述如何应用到实际的项目中,达到提升测试效率的目的。
1、项目难题
这个月开始忙起来了,接了个大项目,苦不堪言,以下3个问题应该大部分测试人员都能感同身受,并且也是经常会遇到的问题
- 测试时间被压缩
- 测试资源被砍
- 业务流程长
2、解决方案
针对前面2点,除了猛加班,还能有更好的解决方案吗?只要你够肝就能完美应对前面2点问题
针对第3点,只能英勇献身,主动写脚本帮助团队造数据,以前是用jmeter做接口自动化,但在接触了httprunner后,发现该测试框架比jmeter方便很多,因为它有.har直接转换成用例的功能,所以就采用了httprunner框架。
二、设计自动化测试用例结构
1、项目系统流程图

图1:项目系统流程图
2、用例分层
2.1 简要说明
该项目范围是打版模块,根据系统流程图可以知道,打版的数据需要走2个系统的交互,共涉及3个大模块。应项目要求,将需求单录入->开发bom的流程实现接口自动化用例,对应功能步骤为需求单录入->商品确认成功。
2.2 官网用例分层示例图
分层思想如下:
- 测试用例(testcase)应该是完整且独立的,每条测试用例应该是都可以独立运行的
- 测试用例是测试步骤(teststep)的有序集合
- 测试用例集(testsuite)是测试用例的无序集合,集合中的测试用例应该都是相互独立,不存在先后依赖关系的;如果确实存在先后依赖关系,那就需要在测试用例中完成依赖的处理

图2:官网分层示例图
3)项目用例分层架构
该项目用例分层架构图是根据自己的理解做的用例分层,如果各位大佬有其他好的用例拆分设计可以赐教下。
如图3,共有5条用例,用例跟用例之间有调用关系,因在测试用例中已经完成了用例依赖处理,所以testsuite的每条用例都是可以直接运行的,不存在先后顺序。
本项目存在个问题,应当都会存在这种问题,即当case3调用case2,若case2用例存在问题,那case3自然就会执行失败。

图3:项目用例分层架构图
三、项目源码解析
1、.env文件说明
env主要是存放环境配置的信息,比如url或者用户名密码之类的
username=李白
password=123456
url=https://xxx.com
2、登录Case1:login_test.py
- 知识点1:结合实际情况,若有多个测试环境,url是会变的,用户名和密码也可能会频繁更换,所以可以考虑将url和用户名密码放在.env文件中,达到可配置化
- 知识点2:.env文件的变量取用:${ENV(文件内的键名)},详见下面标黄部分
# NOTE: Generated By HttpRunner v3.1.5
# FROM: har\login.har from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase class TestCaseLogin(HttpRunner): config = Config("登录").verify(False) teststeps = [
Step(
RunRequest("登录接口")
.post("https://xx.com/login")
.with_headers(
**{
"content-length": "183",
"referer": "${ENV(url)}", #引用.env文件的url变量
}
)
.with_json(
{
"employeeName": "${ENV(username)}", #引用.env文件的username变量
"password": "${ENV(password)}", #引用.env文件的password变量
}
)
.extract()
.with_jmespath("body.data.userId", "userId") #提取userid,userNo,userName供后续用例使用
.with_jmespath("body.data.userNo", "userNo")
.with_jmespath("body.data.userName", "userName")
.validate()
.assert_equal("status_code", 200)
),
]
if __name__ == "__main__":
TestCaseBaibuLogin().test_start()
3、需求单Case2:demand_test.py
- from testcases import login_test:该用例需要先调用登录用例,如果想要引用别的文件的用例,需要先import进来
- RunTestCase:前面的文章讲过,这是直接调用别的用例,引用方式为.call(文件的类方法)
- headers:该用例共有3个step,请求头是一样的,所以可以用一个变量存起来,便于后面step引用
- .with_jmespath("body.data.list[0].styleCode","styleCode"):类似jmter的jsonpath写法,可以提取想要的响应字段,用于后面用例的引用
# NOTE: Generated By HttpRunner v3.1.5
# FROM: har\需求单.har from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
from testcases import login_test class TestCase需求单(HttpRunner): config = Config("新建需求单-需求单列表-设计单").verify(False).base_url("https://xxx.cn")
headers = {"Connection": "keep-alive",
"Content-Length": "1480",
} teststeps = [
Step(
RunTestCase("调用登录用例").call(login_test.TestCaseLogin)
),
Step(
RunRequest("新建需求单")
.put("/demand-task")
.with_headers(
**headers
)
.with_cookies(
**{
"experimentation_subject_id": "ImY3ZG"
}
)
.with_json(
{"deliveryTypePeriod": "2",
"remark": "需求单",
......
}
)
.extract()
.with_jmespath("body.data.demandDetailId","demandDetailId") #提取detaildid,taskid供后续用例使用
.with_jmespath("body.data.demandTaskId","demandTaskId")
.validate()
.assert_equal("status_code", 200)
),
Step(
RunRequest("需求列表接口")
.post("/list")
.with_headers(
**headers
)
.with_json(
{
"demandTaskType": "",
}
)
.extract()
.with_jmespath("body.data.list[0].styleCode","styleCode")
.validate()
.assert_equal("status_code", 200)
),
Step(
RunRequest("设计单接口")
.put(
"/task/${demandDetailId}" #引用前面提取的detaildid
)
.with_headers(
**headers
)
)
.with_json(
{
"current": "178",
}
)
.validate()
.assert_equal("status_code", 200)
),
] if __name__ == "__main__":
TestCase需求单().test_start()
4、设计拆单Case3:design_test.py
- export(*["styleCode","demandTaskId"]):这里需要用到case2提取出来的参数,所以要用.export导出参数变量,供case3引用,引用方式为 ${参数名}
- teardown_hook("${sleep(2)}"):case2和case3所属的模块归属于不同的服务,case2服务完成设计单推送到case3服务时会存在回调时间差的情况,
- 所以需要使用teardown_hook,让用例执行完后,等待几秒再运行后面的step。(原理是调用debugtakl.py的sleep函数,达到延迟执行的目的)
# NOTE: Generated By HttpRunner v3.1.5
# FROM: har\设计拆单.har from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
from testcases import demand_test class TestCase设计拆单(HttpRunner): config = Config("设计拆单列表-提交拆单").verify(False).base_url("https:xxx.cn")
headers = {"Connection": "keep-alive",
}
teststeps = [
Step(
RunTestCase("调用需求单用例Case2").call(demand_test.TestCase需求单).export(*["styleCode","demandTaskId"]).teardown_hook("${sleep(2)}")
),
Step(
RunRequest("设计拆单列表接口")
.post("type/list")
.with_headers(
**headers
)
.with_json(
{
"prototypeStatus": "1",
}
)
.extract()
.with_jmespath("body.data.list[0].prototypeId","prototypeId")
.with_jmespath("body.data.list[0].designCode","designCode")
.validate()
.assert_equal("status_code", 200)
),
Step(
RunRequest("提交拆单接口")
.put("/save")
.with_headers(
**headers
)
)
.with_json(
{
"prototypeId": "${prototypeId}","styleCode": "${styleCode}",
"demandTaskId": "${demandTaskId}",
"designCode": "${designCode}",
}
)
.validate()
.assert_equal("status_code", 200)
)
] if __name__ == "__main__":
TestCase设计拆单().test_start()
5、商品跟进确认Case4:goods_test.py
# NOTE: Generated By HttpRunner v3.1.5
# FROM: har\商品跟进确认.har from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
from testcases import design_test,confirm_test class TestCase商品跟进确认(HttpRunner): config = Config("商品跟进列表-获取匹配结果-商品确认-商品跟进提交").verify(False).base_url("https://xxx.cn") teststeps = [
Step(
RunTestCase("调用设计拆单用例Case3").call(design_test.TestCase设计拆单).teardown_hook("${sleep(2)}").export(*["designCode"])
),
Step(
#因case5和case4属于不同的系统,即不同服务,所以匹配成功后,存在推送时间差问题,故执行完用例后需等待几秒
RunTestCase("调用匹配用例Case5").call(confirm_test.TestCase匹配).teardown_hook("${sleep(3)}")
),
Step(
RunRequest("商品跟进列表接口")
.post("/goods-track/list")
.with_headers(
**{"Connection": "keep-alive",
}
)
.with_json(
{"designCode": "${designCode}","pageNum": 1,
"pageSize": 20,
}
)
.extract()
.with_jmespath("body.data.list[0].materialTrackId","materialTrackId")
.validate()
.assert_equal("status_code", 200)
),
Step(
RunRequest("获取匹配结果")
.get(
"/details/${materialTrackId}"
)
.with_params(
**{"workerId": "178",
}
)
.with_headers(
**{"Connection": "keep-alive",
}
)
.extract()
.with_jmespath("body.data.demandList[0].matchResultList[0].matchResultId","matchResultId")
.validate()
.assert_equal("status_code", 200)
),
Step(
RunRequest("商品确认接口")
.post(
"/result/confirm"
)
.with_headers(
**{"Connection": "keep-alive",
}
)
.with_json(
{
"materialTrackId": "${materialTrackId}",
"matchResultId": "${matchResultId}",
}
)
.validate()
.assert_equal("status_code", 200)
),
Step(
RunRequest("商品跟进提交接口")
.post(
"/goods/submit"
)
.with_headers(
**{"Connection": "keep-alive",
}
)
.with_json(
{
"materialTrackId": "${materialTrackId}",
"updateList": [
{
"materialTrackId": "${materialTrackId}",
"matchResultId": "${matchResultId}",
}
],
}
)
.validate()
.assert_equal("status_code", 200)
),
] if __name__ == "__main__":
TestCase商品跟进确认().test_start()
6、匹配Case5:confirm_test.py
# NOTE: Generated By HttpRunner v3.1.5
# FROM: har\匹配.har from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase class TestCase匹配(HttpRunner): config = Config("匹配列表-匹配结果").verify(False).base_url("https://xxx.cn") teststeps = [
Step(
RunRequest("匹配列表接口")
.post("/list")
.with_headers(
**{"Connection": "keep-alive",
}
)
.with_json(
{"pageNum": 1,
"pageSize": 10,
}
)
.extract()
.with_jmespath("body.data.list[0].demandId","demandId")
.validate()
.assert_equal("status_code", 200)
),
Step(
RunRequest("匹配结果接口")
.post("/match/create")
.with_headers(
**{"Connection": "keep-alive",
"Content-Length": "577",
}
)
.with_json(
{
"bottomCloth": "","colorNumber": "1.0",
"commodityCode": "PURE",
}
)
.validate()
.assert_equal("status_code", 200)
),
] if __name__ == "__main__":
TestCase匹配().test_start()
四、结束语
以上就是本项目运用httprunner实现接口自动化的全过程,因涉及到公司安全,所以源码中删减了很多接口信息,但这并不会影响整个思路的梳理哈,本次项目涉及到的知识点比较少,后续新项目如果有其他知识点运用,会继续更新进来,还有因为想要的参数 接口都有返回,所以这次没有用到数据库,如果对数据库连接感兴趣的,可以看我前面的博客,以及因项目原因,本次的断言信息基本是用它自动生成的断言,没有增加新的断言,但一般接口自动化中是需要跟落表信息或者关键id信息进行断言的。
HttpRunner3.X - 全面讲解如何落地项目实战的更多相关文章
- 项目实战 - 原理讲解<-> Keras框架搭建Mtcnn人脸检测平台
Mtcnn它是2016年中国科学院深圳研究院提出的用于人脸检测任务的多任务神经网络模型,该模型主要采用了三个级联的网络,采用候选框加分类器的思想,进行快速高效的人脸检测.这三个级联的网络分别是快速生成 ...
- 【无私分享:ASP.NET CORE 项目实战(第十三章)】Asp.net Core 使用MyCat分布式数据库,实现读写分离
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 MyCat2.0版本很快就发布了,关于MyCat的动态和一些问题,大家可以加一下MyCat的官方QQ群:106088787.我 ...
- 【无私分享:ASP.NET CORE 项目实战(第六章)】读取配置文件(一) appsettings.json
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 在我们之前的Asp.net mvc 开发中,一提到配置文件,我们不由的想到 web.config 和 app.config,在 ...
- 【无私分享:ASP.NET CORE 项目实战(第五章)】Repository仓储 UnitofWork
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 本章我们来创建仓储类Repository 并且引入 UnitOfWork 我对UnitOfWork的一些理解 UnitOfW ...
- webpack 教程 那些事儿04-webpack项目实战分析
这节主要讲解真正项目用用到的 webpack配置问题,项目实战篇 就像我们不会完全做一个项目,不用别人的轮子一样.这个配置我们借用 vue-cli 搭建的配置来研究,因为它已经足够优秀. 有了前面的基 ...
- 云计算Docker全面项目实战(Maven+Jenkins、日志管理ELK、WordPress博客镜像)
2013年,云计算领域从此多了一个名词“Docker”.以轻量著称,更好的去解决应用打包和部署.之前我们一直在构建Iaas,但通过Iaas去实现统一功 能还是相当复杂得,并且维护复杂.将特殊性封装到 ...
- Spark大型项目实战:电商用户行为分析大数据平台
本项目主要讲解了一套应用于互联网电商企业中,使用Java.Spark等技术开发的大数据统计分析平台,对电商网站的各种用户行为(访问行为.页面跳转行为.购物行为.广告点击行为等)进行复杂的分析.用统计分 ...
- Solr集群、KI分词、项目实战
Solr是一个高性能,采用Java开发,基于Lucene的全文搜索服务器.同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置.可扩展并对查询性能进行了优化,并且提供了一个完善 ...
- NET 分布式架构开发项目实战
.NET 分布式架构开发项目实战 从头到尾,一步一步讲述一个真实的项目实战,关注点主要是架构的思考和实现,以及如何解决平时项目遇到的一些问题. 同时也司公布源代码. 如何构建高性能,稳定SOA应用之- ...
随机推荐
- C#设计模式---模板方法模式(Template Method Pattern)
一.目的 模板方法模式把不变行为搬到超类中,从而去除了子类中的重复代码. 二.定义 模板方法模式:在一个抽象类中定义一个操作的算法骨架,将算法骨架中某些特定的操作延迟到子类中实现. 模板方法使得子类在 ...
- mysql ORDER BY 中文出现错误问题
在MySQL中,我们经常会对一个字段进行排序查询,但进行中文排序和查找的时候,对汉字的排序和查找结果往往都是错误的. 这种情况在MySQL的很多版本中都存在. 如果这个问题不解决,那么MySQL将无法 ...
- 设置Sublime插件快捷键--实现CSS颜色选取
安装插件ColorPicker 如果你经常要查看或设置颜色值,这个插件可以很方便地调用你本机的调色板应用.(译者扩充:)这是一个双向的功能,你既可以在调色板中选择一个颜色,然后按"确定&qu ...
- REST设计风格:你写的 RESTful API 到第几层了?
理解REST 在理解其真正概念前,我们先来明确: REST它的核心思想是面向资源的抽象(相对于RPC就是面向过程抽象),它是一种设计风格的指导,而非具有较强约束的协议. REST源于Roy Thoma ...
- HCNP Routing&Switching之OSPF虚连接
前文我们了解了OSPF的网络类型.帧中继交换机映射以及路由器帧中继映射相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15195762.html:今天我 ...
- ORB_SLAM2 闭环检测段错误
问题描述: Ubuntu14.04运行正常.Ubuntu 16.04下运行时,检测到闭环后有时会段错误,定位发现断错误出现在CorrectLoop()的红色代码处 void LoopClosing:: ...
- Shell 脚本如何输出帮助信息?
作者展示了一个技巧,将帮助信息写在 Bash 脚本脚本的头部,然后只要执行"脚本名 + help",就能输出这段帮助信息 https://samizdat.dev/help-mes ...
- 20210816 你相信引力吗,marshland,party?,半夜
考场 第一眼都不可做 T1 长得就像单调栈/单调队列,推了推性质发现优弧.劣弧都合法的点对很好处理,其他情况只在一种情况合法,那么开两个单调队列分别统计距离 \(\le\frac2n,>\fra ...
- B. 2194: 快速傅立叶之二解题报告
$$\begin{eqnarray}&c[k] = \sum_{i}^{n}a[i]b[i-k] \\&c[k] = \sum_{i}^{n}a[n-i]b[i-k] (倒序保存a) ...
- JS014. toFixed( )调试踩坑 - 浏览器思维 点常量 & 点运算符
Number.prototype.toFixed( ) 在观察toFixed()丢失精度问题,和对toFixed()方法重写的调试过程时,发现toFixed()对Number的识别有它自己的规则,并找 ...