专注测试技术的课程订阅站点

最恶心的测试用例--现实生活中的反模式测试行为

看到一篇吐槽测试用例的文章https://itnext.io/the-worst-test-suite-testing-anti-patterns-experienced-in-real-life-24fd13ee3ddd,觉得挺有意思的,没啥新意,不过也确实是我们会遇到的问题,或者说是我们生活的一部分。顺手翻译了一下,下面是文章内容。

我认为这里不需要介绍,我将告诉您我遇到的最糟糕的测试套件(testsuit)是什么样子,以及它是如何运作的。

我提前警告您,这篇文章会让您感到恶心,皮肤发痒,工程技能受挫。

我将介绍的反模式将以恶心程度进行排名,从 🤮 表示糟糕到 🤮🤮🤮 表示完全不可原谅。 所有的反模式都很糟糕,但其中一些我从未在其他地方见过,我认为对它们进行排名将强调它们的隐蔽性。

在阅读本文时,整个套件大约有 10 个测试文件。其中 9 个是“合理的”,第十个则是一场噩梦:它有大约 3000 行代码,总共组合了约 80 个测试用例。测试套件的约 95%都在这个文件中。

除了测试用例之外,测试使用的所有工具都散落在这个文件中。

混合测试类型

恶心程度: 🤮

该文件包含了许多不同类型的测试——从单元测试到组件测试,再到端到端测试。有些测试非常难以确定测试类型——有些单元测试通过运行系统的主要部分来测试特定的小功能,而在其他情况下,通过调用系统的内部功能序列来执行主要的端到端测试。

为什么测试被编号?

恶心程度: 🤮🤮🤮

所有的测试名称都被编号了!所有的测试都采用了 TestXX_what-the-test-checks 的形式。

这是一个重要的警示信号,当我加入这个项目时,我没有完全意识到它的重要性。大约工作了两个月后,显然这些测试之间的顺序是有问题的!这些数字是为了强制排序而存在的。这些测试是相互依赖的。

不用说,当我移动一些测试以使那个庞大的文件变得更清晰时,我是以艰难的方式学到了这一点。

如果我想添加一个测试,它必须与其他测试正确排序。这创造了一种荒谬的情况,我必须正确命名测试以便按正确的顺序运行。因此,如果我想让一个测试在 Test34_XXX 和 Test34_YYY 之间运行,我必须将我的测试命名为 Test341_ZZZ,以确保字典顺序正确 🤯

匿名测试

恶心程度: 🤮

关于测试名称,还有一件事——其中一些是匿名的——这些测试并没有说明它们实际覆盖的内容,例如:test19_requirement_59_passes 或者最受欢迎的 test87_process_works。

有些测试只有在我引入了使它们失败的更改后,我才知道它们测试的是什么,这迫使我进行调查工作以弄清楚它们在做什么。

(这不是原文,这只是我的补充:这种情况就是测试用例的名字没有任何的意义,没人知道这个用例在做什么)

断言?它们只是建议

恶心程度: 🤮🤮🤮

一些测试没有以断言结束。您当然会问一个没有断言的测试在做什么?

在这些测试中,测试的顶部有一条注释,指示“用户”去做某事。通常是像“转到日志文件并检查是否存在格式为 X - Y - Z 的日志消息”。

不用说,这并没有说明日志文件在哪里,如果有多个文件(由于日志轮换配置)该怎么办。而且,在某些情况下,这些说明已经过时,日志消息自“测试”和其说明编写以来已经发生了变化。

这些测试显然总是通过的,项目移交期间没有人告诉我这些事情,我是在添加一个功能时偶然发现有一个测试,其名称表明它测试的是我实现的过时功能的相反面。它显然通过了(因为它没有断言)。

我删除了所有这些测试,再也没有回头看过。

(这不是原文,这只是我的补充:这种情况就是只执行动作不做断言,基本上大部分同学入门的时候都会经历这个阶段。)

复杂和隐晦的输入

恶心程度: 🤮

系统的测试输入相当复杂,大多数测试都基于一个单独的输入文件。除了“刚好足够让测试通过”的输入之外,它包含的内容相当隐晦。没有人真正记得这个测试文件是如何创建的。

为了将每个测试带到相关状态,散布在 3000 行代码中,有一些实用方法来操作输入文件。它们都没有解释它们的作用,通常被称为 prepare_for_test78 之类的名称。

每当我们需要更改输入时,我们都会有点儿心痛 🥲

(这不是原文,这只是我的补充:这种情况就是准备的数据不具备可读性,没人知道这些数据是做什么的。)

拖累的共享状态

🤮 恶心程度: 🤮🤮

QA Leader的职责是什么

很少看到有人讨论 qa leader 的职责,最近正好看到一篇,随手翻译了一下,供大家参考。

在这篇文章中,我将分享我作为测试负责人的经验。

负责人可以是测试负责人、QA 负责人、测试负责人,等等。

虽然职位名称可能不同,但我负责领导一个测试团队。

根据我大约 20 年的经验,我列出了负责人 QA 工程师的任务。

负责人扮演着双重角色。

他/她既是实际参与者,也是管理者。

实际参与者意味着他编写测试用例,运行它们,报告错误,进行缺陷审查等等。

这些任务都是由个人贡献者完成的。对吧?

除此之外,负责人还承担其他任务。

这比个人贡献者更多。他/她是产品/项目的其他利益相关者眼中的团队代表。

现在,让我们详细了解这些任务。

作为个人贡献者的活动

QA 负责人的主要任务是测试。

也就是实际参与者。通常,负责人的工作分配大约是 70-30%的时间。

也就是 70%作为个人测试人员,30%作为负责人的活动。

这可能因项目而异。

好了,现在作为手动测试人员,负责人会做以下工作:

  • 实际测试
  • 编写测试用例,
  • 执行测试用例。
  • 编写自动化脚本,
  • 审查测试用例/自动化代码
  • 执行脚本
  • 提 bug
  • 文档编写

你可能会说这些都是由每个团队成员完成的。对吧?

作为负责人的活动

现在,让我们看看负责人特定的任务。

我还列出了负责人的责任。

  • 分配任务。
  • 进行工作量估计。
  • 对测试负责。
  • 通过支持他们来保护测试人员。
  • 对估算的任何延误负责。
  • 确保分配的任务完成。
  • 有点像夹心饼干 - 位于管理层和团队之间!
  • 准备不同的文档,如测试策略、测试计划。
  • 还准备测试报告、状态报告。
  • 与开发、IT、产品、客户支持团队进行沟通。
  • 帮助招聘和建立自己团队以及其他团队。
  • 在公司和客户层面为测试工具/项目或自动化开展多个概念验证(POC)。
  • 培训初级人员。
  • 在需要专家知识的其他项目中做出贡献,或者只是帮助他们进行指导/审查他们的自动化。因此,这是负责人提供的额外咨询服务。
  • 在其他人不计划休假时支持团队交付工作。也就是说,当需要时,负责人(他/她)会介入并执行任务。
  • 寻找改进测试流程的方法,并为团队创造心理安全环境,使他们能够做出最好的工作。
  • 发布计划和估算。在发布计划和估算中发表意见是 QA 负责人的主要技能和责任。
  • 与支持团队一起解决生产问题。
  • 例如,重现问题,确定延误的根本原因等等。
  • 设计/增强自动化框架

总而言之,QA 负责人领导、指导和监控测试分析、设计、实施和执行,测试程序和测试套件。

常用的adb命令

Android Debug Bridge 是我们比较常用的命令行工具,该工具可以在 Android 设备上执行不同的操作,例如安装或卸载应用程序、输入文本、捕获屏幕截图等,下面是一些常用的 adb 命令及使用场景。

**列出连接的设备**

adb devices

上面的命令可以列出设备的序列号和状态,如果连接了许多设备并且我们想将它们区分开来,我们可以运行带有-l选项的命令以列出有关设备的更多详细信息。

adb devices -l

此命令向我们显示设备、型号等。

**安装应用程序**

通过提供.apk文件的路径,可以在设备上安装应用程序

adb install <path_to_apk>.apk

如果连接了多个设备,直接运行上面的命令是会报错的,这时应使用-s选项指定目标设备的序列号。

adb -s <serial_number> install <path_to_apk>.apk

这里的-s是一个常用选项,用于指定具体的设备,会在 adb 命令中大量使用到。

**列出 package**

adb shell 中的包管理器(package manager)工具可用于列出设备上安装的所有包

adb shell pm list packages

后面接grep命令可以实现更为精确的过滤

adb shell pm list package | grep what_you_want

启动**Activity**

可以用下面的命令来找到指定的 package 中的某个 activity

adb shell dumpsys package | grep <package_name> | grep Activity

然后下面的命令可以启动 activity

adb shell am start <package_name>/<activity_name>

比如下面的命令可以启动 google map

亲历裁员竞赛

今年的年度关键词里大概率会出现裁员这个选项。国内互联网公司风风火火的进行了一整年,最近硅谷也在跟进,根据已经放出风来的口径统计的话,硅谷头部公司大概会裁员 120k 左右,超过了当年互联网泡沫时期的 107k,有分析人士指出,这只是开始而已,这个冬天,裁员的寒风会更加凛冽。

裁员的逻辑

逻辑其实很简单,全球经济不好,互联网增长不及预期,而且找不到新的增长点,这样之前为增长而储备的人力就需要释放,裁员明显是一个很好的选项,所以华尔街出现了一些公司越裁员股价越涨的情况。另外一般以增长为目标的项目或产品,往往投入高收益少,也就是所谓不赚钱的业务,后面裁员的目标将非常聚焦和具体,只要不赚钱,那么走人的可能性就非常大。

被裁之后

首先被裁不是员工自身的问题,所谓的时势造英雄,整个大环境不好让所有人都很难独善其身。被裁之后的心理压力一定是巨大的,管理好情绪开始下一段旅程,这个世界还是充满着无限的可能,不要因为别人的错误而自怨自艾,记住,在这个大的时代背景和叙事框架下,你的被裁大概率是在为别人的失误买单。

风暴中心

这次我其实处在裁员的风暴中心,分享几个事情。

我们部门这次裁员了 75%,之前坐满了一层楼,而且裁员两天后就已经冷冷清清,士气凋零。

跟同事聊天,他说有些毕业生被裁后拿到的赔偿金额够他们生活 1 到 2 年,完全有时间去考公上岸,也算是提前离场,开启下一段人生。

其他部门很多同事人心惶惶,下一个是不是自己无法确定,下一波什么时候到来也让人惴惴不安。

跟国外的同事聊天,他们目前有 11 个人,下个月就只有 10 个人,因为其中 1 人收到了 np,notice period,有兴趣的同学可以查查是什么意思。他们国家今年经济其实发展的不错,但是互联网行业整体不行,其他大厂也在裁员,相当惨烈。安慰了他几句,很默契的互相留了联系方式,因为大家都不知道哪天会突然离开。

配合另一地区的同事进行上线之前的验收,那位同学早上还在提 bug,下午的时候就忽然告诉我他要走了,刚刚被裁,last day 就是今天下班。我无话可说,安慰了几句,互相留了联系方式。感觉像是在玩狼人杀,每天都有人被刀的样子,期待明天会是一个平安夜。他走的时候问我:你觉得今年会不会有年终奖?我的回答是: I doubt。

有的人被裁后很快就找到了工作,也有的人两个月了没什么进展,找工作这个事情因人而异,但总体看来市场上的需求明显不如几年前那么旺盛。

有人觉得压抑,想主动离开,我总是劝留不劝走,从行业内来看,外面的世界无奈大过于精彩。

经常有滚动裁员,量不大,比如前两天又离开了 10%左右,工作的安全感非常欠缺。

总结

其实无话可说,安全第一,共克时艰。

用python实现简单的版本号生成工具

我们发布比较频繁,每次发布都需要从 release 分支打 1 个 tag,不过可能是因为年纪大了的缘故吧,尽管借鉴了一些版本号的制定规则,不过每次我都记不太住,需要翻文档去重新复习,不如把这步自动化一下,为未来的自己节约一点时间,另外有了工具就好统一规则,后面所有相关的项目都可以用同一套规则来生成版本号,去掉了人工对齐的成本,变相提升了效率。

版本号规则

我们的版本号大概长这个样子

v1.00.0-hotfix-20221111-1
版本号   发布类型 发布时间  第几次发布
  • 版本号规则: 大版本 1 位 + 小版本 2 位 + patch 版本号 1,比如 v1.00.0 表示大版本是 1,小版本是 0,patch 版本是 0
  • 发布类型:普通发布/hotfix/adhoc(临时版本)

设计

首先不考虑写页面做系统,其实用 vue 随便写个单页应用是可以很快搞定的,不过没那个必要,用命令行工具就好了,简单快速,而且生成了版本号之后可以调用 githlab(我们公司用私有化部署的版本)api 来自动打 tag,在微服务化当道的今天,手动为每次发布的所有 repo 打版本号本身就不是一个很好的体验。命令行工具在这方面比单页应用更具优势一些。

另外交互式的命令行可以省去很多参数 validation 的工作量,这也应该是考虑的。随便搜索了一下,发现了 1 个名为 inquirer 的库可以很好的满足我的需求。

代码实现

import inquirer
from datetime import datetime

STR_MAP = {
	'regular': '',
	'hotfix': '-hotfix',
	'adhoc': '-adhoc'
}

def build_version(options):
	release_type = STR_MAP[options['release_type'][0]]
	date = datetime.now().strftime('%Y%m%d')
	return f"v{options['version']}{release_type}-{date}-{options['seq']}"

if __name__ == "__main__":

	questions = [
		inquirer.Text("version", message="Please enter a version, for sample 1.00.0", default='2.00.0'),
		inquirer.Checkbox(
			"release_type",
			message="Please select a release type",
			choices=["regular", "hotfix", "adhoc"],
			default=['regular']
		),
		inquirer.Text("seq", message="please enter the sequence", default='1'),
		inquirer.Confirm(
			"correct",
			message="This will generate a tag name. Continue?",
			default=False,
		),
	]
	answers = inquirer.prompt(questions)

	if answers['correct']:
		tag_name = build_version(answers)
		print(tag_name)
	else:
		print('nothing to do')

整体流程非常的简单

python requests的替代者?httpx初体验

python 的 requests 库由于其使用简单,文档丰富成为了很多人在发送 http 请求时候的优选选择。前几天看到了一个类似的实现 httpx,在这里简单使用体验一下,顺便简单分享一下体验心得。

相比较 requests,httpx 支持 sync 和 async 的 API,支持 http1.1 和 http2。httpx 尽最大努力兼容 requests 的 API,这样一来用户从 requests 转换到 httpx 的成本就相对较为低廉了。

基本 API

>>> import httpx
>>> r = httpx.get('https://www.example.org/')
>>> r
<Response [200 OK]>
>>> r.status_code
200
>>> r.headers['content-type']
'text/html; charset=UTF-8'
>>> r.text
'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'

简单扫一圈,满眼都是 requests 当年的样子。下面是 requests 的 API,大家来找茬,看看哪里不一样。

>>> import requests
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
>> r.status_code
200
>> r.headers['content-type']
'application/json; charset=utf8'
>> r.encoding
'utf-8'
>> r.text
'{"type":"User"...'
>> r.json()
{'private_gists': 419, 'total_private_repos': 77, ...}

不能说非常相似,只能说是一模一样。

httpx client

requests 为一组 http 请求提供了 session 对象来进行统一设置和管理,httpx 则相应的提供了 client 对象。我们来对比一下使用方式先。