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

自动化测试覆盖率要到多少才算足够

本文非原创,翻译自https://qahiccupps.blogspot.com/2021/09/693-ok.html

“我们的测试用例中有多少是自动化的?”

这个问题有很多内容,特别是当它是监控测试状态的指标的时候。这也不是我第一次被问到。根据我的经验,当有人提出这个问题时,可能是因为(a)他们听说过它,(b)测试用例的数量是可数的,并且(c)他们的任务是提供一个管理层可以接受的数字,并且每月向大量不会看它的人发邮件做 ppt,然后阐述自动化“测试”的价值。

如果这听起来很愤世嫉俗……好吧,我想是的。

但是,并不意味着我对了解您的需求并试图帮助你获得可以你需求的东西不感兴趣。

我们能谈谈你的追求吗?为什么?

我们可以?太好了!

我会开始。我对这个问题的一些考量是:

  • 自动化测试覆盖率似乎被认为是我们测试的一种衡量标准
  • 这样的数字不会说明做过的测试的价值
  • 测试用例的定义没有实际意义
  • …而且,不管它们是什么,测试用例只是我们测试的一部分
  • 有一个隐含的假设,即自动化程度越高越好
  • …但自动化有其自身的风险
  • …而且,无论自动化意味着什么,自动化测试用例只是我们测试自动化的一部分

如果我看看我们的测试方式,以及我们可能称之为测试用例的内容,我现在可以想到三种方式来回答您的问题:

  1. 从我认为问题的意图来看,我们没有测试用例。我们所有正在进行的测试都是探索性测试,虽然我们可能会用自动化的方式记录测试结果,测试完成以后我们把现存的用例转换成自动化是没有意义的,我们标记为得分为 0%。
  2. 出于练习的目的,我准备将回归测试用例集中的每个断言描述为一个测试用例。因为它们将是我们唯一的测试用例,而且它们都是自动化的。得分 100%!
  3. 好的,我们在测试用例管理系统中有一些项目。这些是历史的发布验证记录,(大多数)测试团队以外的人在我们发布之前会进行这些检查。我更喜欢将它们视为检查点,但我很现实,并且知道我的一些同事只是想遵循步骤。相对于“自动化测试用例”的数量,它们很少,但如果我们将它们包括在我们的计算中,我们会将分数降低到 99%。

这些答案对我们中的任何一个来说似乎都不是很令人满意,是吗?

对我来说,这种指标充其量只涵盖了我们所做工作的一小部分,其背后的假设非常值得怀疑。对您而言,该指标的重要性不及某个合理的数字,该数字可以在 ppt 中用来量化测试的最终效果。对此我也有一些想法:

  • 对我来说,测试是知识性的工作,因此很难用简单的数字来衡量
  • 测试不是独立于其他产品开发活动而存在的
  • 如果没有创建测试用例等人工产出物的话我们也可以完成良好的测试
  • 没有经过对话交流和正当理由而强加的指标可能会受到怀疑
  • 强加的指标可能会有被造假的风险
  • 从产出物(bug 列表,用例设计)开始是本末倒置
  • … 最好先问你想测量的指标是什么以及为什么

那么,例如,是否希望衡量客户对产品的满意度?是为了衡量测试的贡献吗?是否要查看时间花在了企业想要停止的活动上?是寻找瓶颈吗?或者是其他东西?如果我们同意使用某种指标,我们如何向测试人员保证他们没有受到不公正的评价,并且他们不应该为了使数字看更好而修饰或者歪曲他们的工作实践?

我们需要的不仅仅是花言巧语。想象一下,你被告知你的表现将根据你发送的电子邮件数量来衡量,你会如何反应?你会在讥讽它的同时但还是发送更多的电子邮件吗?您会发送电子邮件而不是进行丁丁或者是企业微信的沟通吗?您会关心对您、他人和企业的潜在不利影响吗?别人怎么能说服你采取不同的行为?最后,您是否真的希望以良好的意图研究合理的指标并根据结果采取行动?如果是这样,那么我将尽我所能帮助获得一些合理的东西,有明确的警告,公平的,透明的,承认其收集涉及的混乱,可以有效地从数据中得出我们有,这在商定的误差范围内,这反映了我们正在做的工作。如果没有,那么我会问你什么样的数字会更好的反映出实际的工作效果?我会简单地告诉你:让我们说 69.3%,好吗?

作者使用的英文没有那么通俗,加上翻译能力有限,所以最后可能需要简单的提炼一下文章的观点

  • 自动化测试的覆盖率应该不能作为汇报测试效果的指标
  • 测试的目的不是为了产出好看的数据或者指标
  • 不合理的指标会造成不合理的结果而忽略了测试工作的实质

索引构造与信息检索 让ChatGPT成为Selenium问答助手

这是 chatgpt 为我生成的 3 个标题,我选了第 3 个。

  1. 利用 Langchain 和 GPT 实现 Selenium 机器人自动问答
  2. 向量化存储和检索:如何用相似度搜索匹配 Selenium 知识?
  3. 索引构造与信息检索:让 ChatGPT 成为 Selenium 问答助手

之前有很多同学会问我一些有关 selenium 的问题,因为精力有限,不能一一回答。最近正好 chatgpt 为代表的生成式 ai 非常火爆,那么我们能不能训练一个有 selenium 知识的 机器人帮我回答问题呢?人的时间有限,但是机器人可以一直在线工作,妥妥的劳模了。

经过一番调研,我发现 langchain 这个库也许可以祝我一臂之力。

整体思路

整理思路很简单,就是 gpt 是预先训练好的模型,有自己的先见知识,这也是为什么我们问它问题它会给出答案的原因,因为它的模型里就固化了这些知识。然而 gpt3 模型用的数据应该是截止到 2021 年的,也就是 2 年多之前的数据了。尽管 selenium 总体得带速度不快,但这两年也多多少少发生了一些变化,直接用 gpt 模型的先见知识可能会造成一些回答不准确的情况。

所以我们可以用 gpt 的两大能力,而不是先见知识。

  • 推断的能力。给出一些上下文,让 gpt 总结和推断问题的答案
  • 生成内容的能力

所以现在问题就变成了如何给 gpt”灌“入最近的 selenium 知识?

其实我们可以在 gpt 的 prompt 里面加上一些实时的准确的上下文,然后让 gpt 去根据上下文内容实时的生成问题的答案,比如下面这个 prompt

你是selenium问答机器人,你的任务是根据我提供的selenium相关信息来回答用户提出的问题。忘掉你之前的学到的知识,只用我提供的信息去回答。如果问题超出了我提供的信息范围,你就回答不知道。

==============
selenium的一些最新的介绍
...
...
...
==============
问题:selenium是什么?

这样一来任务就变成了如何根据用户的问题搜索相关的 selenium 知识? 这里就要用到向量化存储和检索了。

强行为新项目写ui自动化用例是一种什么样的体验

最近接了一个新项目,配套有个 web 管理后台页面,尽管需求一直在迭代以及测试时间相对不宽裕,我还是决定写点自动化用例作为功能测试的补充和回归测试的输入,顺便玩一下 playwright,不在真是项目中使用一种技术其实是很难对这种技术产生深刻理解的。

项目介绍

管理后台是前后端分离的,前端用的 react 加上蚂蚁的前端组件库,后端是基于 golang 构建的微服务。其实这种项目更适合做接口测试,ui 自动化作为补充就好了。

技术选型

这点很清楚,之前 benchmark 过,playwright 比 cypress 性能要好,所以直接选 playwright,另外 playwright 的 python 版本完全安装之后自带了 pytest 和一系列断言,基本上开箱即用,非常方便。另外为什么不用 js 而用 python,主要是因为我用 python 写了一点接口用例,有些数据库操作的代码可以稍微复用一下,所以统一起见用 python+playwright

第一个难点:登录

管理后台接的是 google auth,由于我的账号开启了二次验证,需要收验证码,所以从 ui 上输入用户名和密码登录就走不通了。不过登录的原理基本相同,就是往 cookie 里写一些东西,后面所有的请求都自动带 cookie 到后端,后端通过之前写的那些东西就可以判断用户是谁,什么时候登录失效等。知道了原理后面就是随便试试了,我的代码大概是这样写的

def login(page):
    page.goto("url")
    page.evaluate(f'()=> document.cookie="{COOKIE}"')

其实就是先访问被测页面,然后自动跳到登录页面,这时候去用 js 设置 cookie,之后再访问一次被测页面就可以自动登录了。

cookie 的话可以从浏览器的开发者工具里直接拷贝出来,因为是测试环境,所以 cookie 的有效期很长,基本可以放心使用。

至于如何定期刷新 cookie 其实也不难搞,写个浏览器插件,每次打开被测页面的时候就把 cookie 发到一个自建后台服务,这个服务就是把 cookie 存到 redis 里,在测试用例里直接访问 redis 拿最新的 cookie 就好了。

定位有点麻烦

作为熟练工,定位对我来说应该不会是大问题,然而现在的前端组件层级嵌套厉害,html 的表意性不强,而且 id,name 等比较有标志性的属性也不是很多,踌躇良久之后我决定请前端同学在一些关键的组件上面加上 id 或者 name,尽管他们不是很愿意,但是我倚老卖老,还是让他们从了。

如何加速执行集成测试

今天我看到了一个有趣的 UI 集成测试加速方法,该方法与我想的底层优化、分布式运行或多线程运行方式不同。该作者重新编排了测试用例的执行顺序,删除了一些代码,并从执行流程上进行了优化。这确实很有趣。在本文中,我将简单介绍他的原文,希望对大家有所帮助。

我们将展示如何加速一个客户的构建过程,这样他们可以在相同的预算下多运行 40%的构建。

我必须承认,我是单元测试的超级粉丝。我早期的一些博客文章就是关于单元测试的。

单元测试是快速的、可预测的、而且隔离的。它们被很好地隔离在一个沙盒中,这就是它们的可预测性和快速性。

我们推出了一个版本,用户无法进入登录页面,但这是另一个故事了。我把它命名为“一个没有集成测试和几十个单元测试的项目”。

并不是说我完全停止写单元测试,而是我很少使用它们。大部分是用于可以安全地进行隔离测试的功能,例如一个接受各种输入并返回各种输出的函数。用集成测试来测试可能的值矩阵是不值得的。

想象一下,有一个日历组件,用户可以输入一个日期,从一个下拉日历中选择数据,然后点击一个按钮来获得一个建议的日期。所有的日期都可以是不同的格式,这取决于用户如何输入它们。是否值得为每种可能的日期格式进行集成测试?也许不值得!

然而,我们把输入的日期发送到一个负责解释许多不同格式的输入日期的实用函数,但它总是返回一个 Date 对象。为这个实用函数写一个单元测试将是一个很好的主意!对于整个应用来说,就不是那么回事了。

假设你正在建立一个问题跟踪器。你有一个集成测试,打开一个对话框,试图保存一个里程碑而不指定名称。这将导致一个错误。你的另一个测试试图创建一个里程碑,但现在指定了一个名称。这不会导致错误,并显示消息“新的里程碑已保存”。

beforeEach(async () => {
  await createNewProject();
});

it("cannot create milestone without a name", () => {
  clickNewMilestoneButton();
  newMilestoneDialog.clickSave();
  newMilestoneDialog.should("have.text", "Milestone name is required");
});

it("creates a milestone", () => {
  clickNewMilestoneButton();
  newMilestonDialog.getNameField().type("February Milestone");
  newMilestoneDialog.clickSave();
  newMilestoneDialog.should("have.text", "New milestone saved");
});

乍一看,上面的测试没有问题。然而,从零开始创建一个新项目涉及设置名称,填写一些起始/结束日期,键入描述,从下拉列表中选择项目所有者,为项目提供相同的标签,填写其他几个字段。我们的测试运行器(我现在使用 Cypress)必须单击所有这些字段,键入一些内容,等待一些建议出现,然后单击,再等待。

或者,将运行时间放在注释里,像这样想象:

beforeEach(async () => {
  // 运行10秒
  await createNewProject();
});

it("cannot create milestone without a name", () => {
  // 运行2秒
  clickNewMilestoneButton();
  newMilestoneDialog.clickSave();
  newMilestoneDialog.should("have.text", "Milestone name is required");
});

it("creates a milestone", () => {
  // 运行3秒
  clickNewMilestoneButton();
  newMilestonDialog.getNameField().type("February Milestone");
  newMilestoneDialog.clickSave();
  newMilestoneDialog.should("have.text", "New milestone saved");
});

整个测试套件运行 25 秒(2x10 + 2 +3),但我们感兴趣的事情只占了其中的 5 秒(2 + 3)。

放弃postman?一个月4k star?接口测试工具hoppscotch评测

天下苦 postman 久矣!

记得当初 potman 刚横空出世时,其形态只是一个浏览器扩展而已,尽管功能简单,不过却带来了另一个非常大的优势,那就是软件体积非常小,安装到浏览器上以后可以借助于浏览器同步的功能,实现各种跨平台支持,特别是对我我这种拥有 win/mac/iinux 的人来说,方便快捷是第一位的。

后来 postman 推出了独立的桌面版本,功能逐渐迭代,性能差的慢慢变态,现在劝退我的是两点:启动速度慢和我用不到的功能慢慢变多;当然除了这两点外还有个相当大的槽点是:postman 会想方设法让你登录,如果你不小心使用了同步功能的话,你的测试文件会公开分享到 postman 上供人品评,这是一个巨大的安全隐患。

D 轮融了 2.25 亿美金,postman 注定要在商业化的道路上越走越远,注定会增加很多我不需要的功能,各种同步,花式协作,满屏的效率提升,不厌其烦的提示我升级等等,对我来说其实需求很简单,只要可以让我朴素的调试接口就可以了。

于是各种 postman 的替代工具应运而生,比如 postwoman,insomnia 等等,这种工具的技术栈都差不多,都是用 js 开发的类似于原生客户端的跨平台工具,今天给大家带来的是一款很火的开源 postman 替代工具: hoppscotch,这个工具在 github 上目前有 40,000 的 star,3 月份新增 4000 的 star,应该是目前最火的测试工具了。

安装

hoppscotch 只需要安装一个浏览器扩展就可以了,支持 chrome 和 firefox。比 postman 动则上百兆的安装包来说,安装过程简单了不少。

安装好扩展之后访问https://hoppscotch.io/就可以使用了。

UI

hoppscotch 的界面跟 postman 差不多,会用 postman 的同学应该会感到比较亲切。

功能

功能上 hoppscotch 也跟 postman 不相上下

  • 支持 rest api 调试
  • 支持 GraphQL 语法
  • 支持 websocket 和 socket.io
  • 支持从 collection 生成文档,这个我不会用
  • 支持 collection 的创建及导出
  • 支持多种 Authorization 方式
  • 支持 pre-request script
  • 支持断言,跟 postman 的写法不能说很像,只能说是一摸一样
  • 完善的快捷键支持
  • 支持 pwa,轻量化的网页解决方案,让网页应用的体验跟 desktop 一样,再也不用忍受 postman 的龟速启动了
  • 支持 proxy,支持自建 proxy
  • 个性化定制:白天模式和暗夜模式,各种颜色主题,可以调整字体大小
  • 支持 cli,这个真是没想到,看了一眼是 go 写的,功能有限,不过可以在命令行运行 collection 了,不过似乎不支持 websocket
  • 完全开源,前端应该是 vue 写的,有开发能力的同学可以进行定制

部署

我们可以在远程服务器上部署个 hoppscotch 版本,然后远程进行访问。不过用 docker 部署的话似乎有点问题,就是浏览器插件无法识别的问题,于是就需要部署个 proxy 用来转发请求,这样就会出现本地 localhost 无法解析的问题,所以如果不是必须的话,用 pwa 的版本体验上就已经很好了。

跟AI一起结对编程,测试驱动开发

martin fowler的博客上新发了一篇关于 AI 和 TDD 的文章,先翻译一下,然后聊聊我的看法。

使用测试驱动开发(TDD)与 GitHub Copilot 编码助手

AI 编程助手如 GitHub Copilot 的出现意味着我们不再需要测试了吗?TDD 将过时吗?为了回答这个问题,我们来看一下 TDD 如何帮助软件开发的两点:提供良好的反馈,以及在解决问题时“分治法”。

TDD 提供良好的反馈 良好的反馈要快速和准确。在这两方面,没有什么能比一个编写良好的单元测试更好。不论是手动测试,文档,代码审查,甚至是生成式 AI,都不能取代。事实上,大型语言模型提供无关信息,甚至 hallucinate。当使用 AI 编程助手时,尤其需要 TDD。我们需要对自己编写的代码快速准确的反馈,也同样需要对 AI 编程助手编写的代码快速准确的反馈。

TDD 通过分治法解决问题 通过分治法解决问题意味着较小的问题可以比较大的问题更早解决。这实现了持续集成,基于主线的开发,最终实现持续交付。但是如果 AI 助手为我们编写了代码,我们是否真的还需要所有这些?

是的。大语言模型很少能在一次提示后就提供我们所需的确切功能。所以迭代开发还没有走到尽头。此外,大语言模型似乎在通过思路提示的增量方式解决问题时“激发了推理”(参见相关研究)。基于 LLM 的 AI 编程助手在分治法解决问题时表现最好,而 TDD 是我们在软件开发中所做的。

使用 GitHub Copilot 的 TDD 技巧

Thoughtworks 从年初开始就一直在使用带有 TDD 的 GitHub Copilot。我们的目标是实验、评估和发展一系列围绕使用该工具的有效实践。

0. 开始

从一个空白的测试文件开始并不意味着从一个空白的上下文开始。我们通常从一个带一些粗略笔记的用户故事开始。我们也会与配对伙伴讨论一个起点。

所有这些上下文都是 Copilot 在我们把它放入一个打开的文件之前(例如测试文件顶部)“看不到”的。Copilot 可以处理拼写错误、点式格式、糟糕的语法等等。但是它无法处理一个空白文件。

一些对我们有效的启动上下文示例:

  • ASCII 艺术画布
  • 验收标准
  • 引导假设,例如:
  • “不需要 GUI”
  • “使用面向对象编程”(而不是函数式编程) Copilot 使用打开的文件作为上下文,所以同时保持测试文件和实现文件打开(例如并排)大大提高了 Copilot 的代码补全能力。

1. 红色

我们首先编写一个描述性的测试示例名称。名称越描述性越好,Copilot 代码补全的表现也越好。