pytest 断言技巧
pytest 的断言把 Python 语言简洁的优点发挥的淋漓尽致,因为它用的就是 Python 的标准断言assert。
pytest 的断言把 Python 语言简洁的优点发挥的淋漓尽致,因为它用的就是 Python 的标准断言assert。
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 测试环境困局 测试环境治理专项是在我入职 2 个月接手的活,用“烂摊子”来形容一点也不为过。年久失修,需求基本没有在测试环境使用,想用也有很多问题。人员缺乏信心,之前负责测试环境的同学抱怨极多,外部推不动,内部又没有技术能力支撑,负能量已经蔓延到整个团队。当然,我也是其中一员,这事确实难搞。在技术部测试环境平台化的大方向下,需要每个组出一个接口人来牵头,考虑到我以前写过测试平台,乱七八糟的事都整过,老板就指定了我。 What,测试环境是什么? 测试环境是一套能够支撑业务系统运行的环境,是相对于预发环境和生产环境来说完全隔离的环境。测试环境=应用+数据+配置+依赖服务。应用包含系统应用和中间件等,数据就是测试物料,配置针对测试环境应该有单独的一套,依赖服务要跟外部联调好以后整个环境才是可用的。测试环境治理就是通过内部搭建和外部联调,保障业务系统能够在完全隔离的环境中运行。 Why,为什么要做测试环境? 测试环境是为了隔离。如果只有一套生产环境,那么开发、测试、上线等活动,可能会对线上系统稳定性造成影响,可能会污染线上用户数据,可能引起客户投诉甚至资产损失,所以需要有一套完全隔离的测试环境。 Who,谁来负责测试环境? 每个测试人员都需要具备测试环境搭建能力。当然可以推动运维或研发来搭建测试环境,但是测试人员需要具备搭建能力,能够自主编译,构建,部署,遇到问题能够根据日志做初步定位,将问题抛出推动研发或外部依赖方解决。 When,什么时间做测试环境? 测试环境治理需要前置到研发自测阶段。在实践过程中,测试同学暴露出来最多的问题是,没有时间投入到测试环境搭建中,一方面是缺乏动力,不肯迈出艰难的第一步,另一方面也是跟项目节奏有关,研发就算用测试环境自测,也会在预发联调好以后再提测,到测试手上时,已经有可用的预发环境了,就不会再去用测试环境。“项目时间紧,测试环境不好用,直接用预发测吧”,顺理成章的就成为了很合理的借口。所以需要从提测,前置到,研发自测,做测试环境治理: Where,从哪里入手测试环境? 测试环境治理需要从上往下才能推动,先有文化,才有行动。假如只有某个部门想做测试环境治理,外部依赖基本上是推不动的,一个人呐喊得再愤怒其他人也可能无动于衷。所以得先把测试环境治理这件事,跟老板沟通,从高层入手,把它定义为一项政治任务,从上往下层层递进。 How,怎么做测试环境? 工欲善其事,必先利其器,公司的基建比较完善,给测试人员搭建测试环境降低了很大难度。设想一下,假如只能折腾 Docker 和 K8S,那可能需要专门的运维才能搞得定环境,对测试来说门槛就会比较高了,除了少数技术能力强的,其他同学基本没有什么可操作性。测试环境平台化就是武器。公司有个部署平台叫做 JDOS,日常运维都是在这上面操作,没有运维人员,环境全靠开发人员和测试人员自己整。JDOS 已经是页面化操作了,但它对测试环境没有特殊的应用场景,是个大而全的平台,所以二级部门开了一个测试环境平台,Env,“一款全流程自动化测试环境搭建的利器”。 Env 的核心思想是1+N。1代表稳定分组(分组≈Pod),搭建稳定分组需要经过创建系统,创建应用,创建集群,申请机器,代码编译构建,部署镜像,修改配置等操作,需要选取 master 稳定版本,这样才能对外提供稳定服务。N是克隆1产生的多个分组,相互隔离,能让开发同学复用测试搭建好的环境,完成冒烟自测等过程;也能在多业务联调测试时,通过平台快速搭建一套全链路测试环境。 Env 提供了环境监控的功能。对于搭建好的稳定环境,它可以每天监控机器是否可用,服务是否正常,镜像是否老旧,等等等,让你及时维护环境,保障稳定性。 Env 提供了一键搭建的功能。新克隆的环境,只要点一下“一键搭建”,就会基于稳定分组,自动化搭建一套新环境,大大节约了时间成本。 Env 提供了拓扑画图的功能。将多个应用拖到一张拓扑图里面,根据依赖关系连线,能够直观了解到应用之间的依赖关系。基于拓扑来克隆新环境,也能批量克隆多个应用。假如多项目需求并行迭代,想要多套联调的测试环境,就可以克隆多套新环境。 Env 还解决了一个资源浪费的问题,以前搭建测试环境比较随意,申请资源也很容易,其实会导致很多机器用过后,一直空置在那里。Env 收拢了申请资源入口,基于 Env 来创建稳定分组,只能有 1 个,想要更多必须走审批。而对于克隆的新环境,也会有期限,14 天以后会自动释放,想要延期也必须走审批。 我所负责的第一件事,就是用 Env 来搭建一张拓扑图,把组内所有应用都从 JDOS 迁移到 Env 上,维持稳定环境对外提供支撑对接,克隆环境对内支持多项目并行。 搭建测试环境只是起步,测试环境治理的目标是需求能够使用测试环境。对于外部依赖少的业务模块来说,这相对容易。对于外部依赖很多的业务模块来说,这就很难了。那么该如何推动这件事呢? 第 1 个策略是共建,让研发和测试一起来搭建和维护测试环境。 第 2 个策略是做功能验证,梳理出链路用例,跟踪验证结果,识别卡点问题: 对于卡点问题的处理,把干系人单独拉群对,响应超过 3 天就往上抛,一定会遇到推不动的问题,那么办法只能找自己的上游负责人,层层向上反馈,让上面的负责人去找对方负责人,然后推动对方的下游同学来协助解决。卡点问题记录后需要做问题分类,做好统计,这既是工作量的体现,也是每周进度汇报的依据: 第 3 个策略就是制定 kpi,强制大家使用测试环境完成绩效。 How much,测试环境做到什么程度? 测试环境的 kpi 能反应出测试环境应该做到什么程度,可以使用需求使用测试环境占比和测试环境 bug 占比。需求使用测试环境占比的计算公式是打标了测试环境的提测卡片数/总的提测卡片数,测试在点击开始测试时,选择是否使用测试环境进行打标。测试环境 bug 占比的计算公式是测试环境bug/(测试环境bug+预发环境bug+生产环境bug),在提交缺陷时选择环境类型进行打标。测试环境 bug 占比的意义更大,因为使用测试环境与否,最终的体现还是落在缺陷上面的,比较容易量化,需求使用测试环境占比不是很确定,需求多少功能在测试环境使用了,相对来说不是很好度量,但也可以作为辅助指标进行观察。 ...
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 变量解析 $符号在很多测试框架中都会拿来用做变量解析,大名鼎鼎的 JMeter 就有这个用法。HttpRunner3 也支持$符号,比如: Step( RunRequest("登录") .post("/login") .with_headers(**{"Content-Type": "application/json"}) .with_json({"username": "dongfanger", "password": "123456"}) .extract() .with_jmespath("body.token", "token") .validate() .assert_equal("status_code", 200) ), Step( RunRequest("搜索商品") .get("searchSku?skuName=电子书") .with_headers(**{"token": "$token"}) .extract() .with_jmespath("body.skuId", "skuId") .with_jmespath("body.price", "skuPrice") .validate() .assert_equal("status_code", 200) ), 从登录提取的变量token,在搜索商品中使用$token来引用。 parser.py HttpRunner 对$符号的解析是在 parser.py 文件中实现的: 这些方法可以分为四类: url build_url() 变量 extract_variables() get_mapping_variable() parse_variables_mapping() regex_findall_variables() parse_data() parse_string() 函数 get_mapping_function() parse_function_params() regex_findall_functions() parse_string_value() parse_data() parse_string() 参数化("${parameterize(account.csv)}") parse_parameters() 本文专门针对变量这一块源码来进行剖析。 全局变量池 HttpRunner3 在运行时的所有变量都是存储在__session_variables这个字典中的: 在脚本开始时会把 config 的预设变量加载进来,然后会在执行 Step 测试步骤时,把各个步骤的变量也放到全局变量池里面。 解析过程 在 runner.py 的第 350 行代码能看到解析变量的调用: ...
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 什么是增量覆盖率 如图所示,在 master 分支提交了 HelloController,然后从 master 拉了个新分支 test;提交了第 1 次代码,增加了 WorldController;提交了第 2 次代码,增加了 DonController。增量的获取方式有两种: 版本对比:在分支上,第 2 次提交和第 1 次提交版本对比的增量代码,就是 DonController;第 2 次提交和刚从 master 拉取分支时版本对比的增量代码,除了 DonController 还有 WorldController; 分支对比:把 test 分支和 master 分支进行对比,增量代码就是 DonController 和 WorldController; 增量,就是通过版本对比或分支对比,多出来的新增加的那部分代码。覆盖率是指程序运行以后,有多少代码被执行到了,除以总的代码数算出来的,即覆盖率=执行代码行数/总代码行数。增量覆盖率是针对增量代码来计算的,也就是增量覆盖率=执行代码行数/增量代码行数。 基本实现原理 使用 JaCoCo 的 On-the-fly 模式,以 tcpserver 方式启动,将远程机器的覆盖率数据通过 TCP 通信 dump 到本地 jacoco.exec 文件,二次开发 JaCoCo 源码,实现增量覆盖率分析和输出 HTML 报告。 JaCoCo 默认覆盖率 JaCoCo 是一个开源的覆盖率工具,它的 On-the-fly 模式,无须侵入应用启动脚本,只需在 JVM 中通过-javaagent 参数指定 jar 文件启动的代理程序,代理程序在 ClassLoader 装载一个 class 前判断是否需要注入 class 文件,将统计代码插入 class,覆盖率分析就可以在 JVM 执行的过程中完成。 ...
测试计划(Test Plan),是 JMeter 目录树的初始根节点,它的编辑区如下:
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 条件测试 JUnit5 支持条件注解,根据布尔值判断是否执行测试。 自定义条件 @EnabledIf和@DisabledIf注解用来设置自定义条件,示例: @Test @EnabledIf("customCondition") void enabled() { // ... } @Test @DisabledIf("customCondition") void disabled() { // ... } boolean customCondition() { return true; } 其中 customCondition()方法用来返回布尔值,它可以接受一个 ExtensionContext 类型的参数。如果定义在测试类外部,那么需要是 static 方法。 内置条件 JUnit5 的org.junit.jupiter.api.condition包中内置了一些条件注解。 操作系统条件 @EnabledOnOs和DisabledOnOs,示例: @Test @EnabledOnOs(MAC) void onlyOnMacOs() { // ... } @TestOnMac void testOnMac() { // ... } @Test @EnabledOnOs({ LINUX, MAC }) void onLinuxOrMac() { // ... } @Test @DisabledOnOs(WINDOWS) void notOnWindows() { // ... } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Test @EnabledOnOs(MAC) @interface TestOnMac { } JRE 条件 ...
用过 unittest 的朋友,肯定知道可以借助 DDT 实现参数化。用过 JMeter 的朋友,肯定知道 JMeter 自带了 4 种参数化方式(见参考资料)。pytest 同样支持参数化,而且很简单很实用。
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 什么是 Playwright? Playwright 是一个开源框架,用于对 Web 应用进行端到端测试。 什么是端到端测试? 这里的“端”,不是指 A 模块或 B 模块,而是可以理解为从前端到后端。举个例子,对于登录功能来说,如果只测前端,那么测试范围就是前端页面,可能会对后端接口进行 mock。如果只测后端,那么测试范围就是后端接口等。而端到端呢,就是前端输入用户名密码,点击登录,与后端交互,查询数据库,鉴权,后端返回登录成功,前端进行页面跳转。端到端测试,可以理解为全流程测试,也可以理解为模拟用户场景测试。 Playwright 框架特点 官方网站首页:https://playwright.dev/python/ 总结下重点,一是跨浏览器、跨平台、跨语言;二是自动等待;三是多用户场景测试;四是只需要一次登录;五是可以录制脚本。 快速入门 在官网的顶部菜单可以切换不同编程语言的教程: 本文及后续文章均选择 Python。 安装 安装 Python 的 Playwright,这里结合了 pytest: pip install pytest-playwright 成功后安装浏览器: playwright install 这里也可以不安装浏览器,而是在脚本里面指定现有浏览器路径。 示例 test_example.py import re from playwright.sync_api import Page, expect def test_has_title(page: Page): page.goto("https://playwright.dev/") # Expect a title "to contain" a substring. expect(page).to_have_title(re.compile("Playwright")) def test_get_started_link(page: Page): page.goto("https://playwright.dev/") # Click the get started link. page.get_by_role("link", name="Get started").click() # Expects page to have a heading with the name of Installation. expect(page.get_by_role("heading", name="Installation")).to_be_visible() 执行 pytest 后默认会选择 chrome 在 headless 模式下运行,可以通过日志观察运行过程。 ...
HttpRunner3 的变量可以在测试类的用例配置中通过variables添加,也可以在测试步骤中使用extract()、with_jmespath()提取出来放到变量x,再用$x传递给下一个接口使用,比如登录到下单流程的部分测试脚本如下:
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 从一个问题说起 对于以下测试脚本: 为了能调用进入房间接口,需要从考场接口获取考场 token。为了调用考场接口,需要从登陆接口获取登陆 token。元件说明如下: 学生登录,提取登录${token}传入下个接口参数。 添加 HTTP Header Manager: 考场 token,提取考场${exam_token}传入下个接口参数。 添加 HTTP Header Manager: 进入房间 貌似挺合理,HTTP Header Manager 会给下方的接口添加请求头,运行结果真的如我们所料么? 运行顺序 在回答这个问题之前,有必要搞懂 JMeter 元件的运行顺序,它是由 2 个维度来决定的:从上往下和元件类型。 从上往下,即目录树从上往下。元件类型,分为 3 类: 线程组、逻辑控制器。 取样器。 配置元件、前置处理器、定时器、后置处理器、断言、监听器。 它们的运行顺序如下: 配置元件(如果存在) 前置处理器(如果存在) 定时器(如果存在) 取样器(如果存在) 后置处理器(如果存在且取样器的结果不为空) 断言(如果存在且取样器的结果不为空) 监听器(如果存在且取样器的结果不为空) 换句话说,假设我们新建了 1 个线程,想用这个线程去发请求。 第一步,初始化配置,比如参数化、设置 Header、Cookie 等,用到配置元件。 第二步,可能需要给线程加点参数,比如用户参数,用到前置处理器。 第三步,在发送请求前可能会等待一段时间,用到定时器。 第四步,发送请求,用到取样器。 第五步,可能需要提取响应数据,比如正则表达式提取器、JMESPath 提取器,用到后置处理器。 第六步,验证结果符合预期,用到断言。 第七步,查看请求响应数据和测试结果,用到监听器。 实践指南 对于以下所列元件: JMeter 会按以下步骤运行: 线程组(如果有多个线程组可以在测试计划设置是顺序执行还是同时执行) 简单控制器(父节点) HTTP Cookie 管理器(配置元件) 用户参数(前置处理器) Synchronizing Timer(定时器) HTTP 请求 1(取样器) 正则表达式提取器(后置处理器) 响应断言(断言) HTTP Cookie 管理器(配置元件) 用户参数(前置处理器) Synchronizing Timer(定时器) HTTP 请求 2(取样器) 正则表达式提取器(后置处理器) 响应断言(断言) HTTP 请求 3(取样器) 察看结果树(严格来讲是与第 6 步并行,也就是取样器之后) 作用域 其中有个观察作用域实际效果的关键元件:HTTP 请求 2,它的前后并没有元件,但是也被作用上了。在 JMeter 中,同一层级的元件具有相同的作用域! ...