如何高效使用Gherkin
背景
时间回到2022年,我参与了一个使用了Flutter技术构建的Web前端项目。在这个项目上,我们小组的目标是实施Flutter前端自动化测试。
彼时,Flutter 2.x刚在Web端发力不久,Flutter Web上的应用和生态才刚刚开始,而在这一切激进的技术栈上构建一套自动化方案的需求又迫在眉睫。
在技术选型上,我们使用了类Cucumber测试的方案,使用Gherkin语言构建一套自动化语言步骤库。Gherkin语言有时候又被称为小黄瓜语言,它是第一种有着类似自然语言可读性的业务语言,用来描述业务行为,而不必关心具体的实现细节。它也是一种领域特定的语言,用来定义Cucumber格式的测试。
通过不断地改进,我们使得这些步骤既具有自然语言通俗易懂的可读的特性,又具有自动化步骤的可执行性,用这套步骤,我们最终用它统一了手工测试用例和自动化测试用例的书写,执行,管理。
文档式Gherkin和动作式Gherkin的区别
Gherkin语言其实可以使用不同国家语言的单词和语法书写,但和其他编程语言一样,我们这里还是使用英文单词和文法。
当定义步骤库的时候,使用Gherkin语言去描述业务或者用例可以有不同的风格,典型的有“文档式Gherkin”和“动作式Gherkin”这两大类。
文档式Gherkin往往用来描述“应该做些什么”。所以经常用来描述软件需求,产品期望行为。
比如一个步骤是:“当创建了一个新用户的时候,那么他会出现在新用户列表中”。这种风格的好处就是可以快速书写出一个结构合适,方便理解的软件文档。当这种文档式Gherkin语言写的测试执行失败的时候,往往代表了产品的实际行为和文档上的期望行为发生了背离。当然文档式Gherkin语句也有其缺点,比如自动化实现起来,某些语句需要验证的范围会非常大,执行复杂,且随着测试的增加,步骤库里的步骤数量也会快速地增长。
相比文档式Gherkin,动作式Gherkin描述的是“如何做些什么”,由于动作式Gherkin关注的是每一步具体做什么,所以常用作写测试用例。如一个步骤是“当点击含有Submit文字的按钮,那么Successfully文字应该可见”。
动作式Gherkin语句的好处就是目的单一,每一步需要验证的点很小,当然与之而来的缺点就是要完成一个用例的书写需要很多步骤组成,用例里的步骤会很多,用例变得很长。且每一个动作对测试场景的覆盖率都不高,需要完成测试覆盖率要很多步骤一同拼凑起来。
文档式Gherkin风格详解
由于我们需要大量自动化测试用例,所以我们更倾向于使用动作式Gherkin,虽然最终我们使用了动作式的Gherkin语言定义了自动化步骤库,但我们还是先了解一下文档式Gherkin的风格。
文档式Gherkin使用描述性的措辞,聚合了具体的动作。这使读者能够快速理解一个场景,并掌握文档中描述的软件功能。使用文档式Gherkin语言写的软件需求或者测试,并由自动化实现执行后,这个文档基本上不会过时。因为一旦软件实际行为和需求文档的描述发生了背离,那么自动化执行需求文档上的Gherkin语句的时候,测试就会失败。而这些测试往往都是以天为粒度去执行的,如果今晚你提交了代码改变软件行为而没有更新需求文档,当晚的自动化测试流水线就会失败红掉。如果你的项目有流水线”红不过夜”(导致流水线失败变红的问题不留到第二天而是当天内解决)的规定,那么恭喜,今晚就必须把文档上由Gherkin语言书写的测试修正。
使用场景:书写可测试的软件需求说明书
例子:
When the admin creates a new user
Then the user list should contain the newly created user
规则1:使用不大于5个步骤的Scenario场景
Scenario是Gherkin语言中的关键字,通常代表一个场景。作为一个经验法则,一个文档式Gherkin写的场景通常由3-5个步骤组成。有时候,就连包含Given的步骤也不需要,那么便只需要2个左右步骤了。当然,长的场景可能包含了多于5个Gherkin步骤,此时便可能说明了这个场景需要拆分了,让更多更小的场景去覆盖每一个需要关注的点。
同时,前置条件也是可以隐式说明的。比如,在描述产品行为的时候,对于每个与登录页面本身无关的场景,可以预期用户已经登录了。每一个场景一般只应该包含一个“Then”。
如果有多个包含Then的步骤,那么就说明这个场景有多个AC(Acceptance Criteria)。如果Then语句执行失败,那么此AC便校验失败了,测试便会停止,那么后面的AC步骤也不会去执行验证,于是在失败的路径下多个AC便失去了意义。当然这个规则也不是个教条,比如当两个AC相互依赖,他们最好同时验证。比如分开验证两个AC都时候时间成本,外部资源成本非常昂贵,那么也是可以放在一起写多个Then语句的。
规则2:使用主动语态
当描述一些行为的时候,应当使用主动的语态,比如 “the user does X” 而不是 "X is done by the user"这种被动的语态。
规则3:使用不同时态
在Gherkin中定义包含Given语句的时候,要使用过去时时态,因为这表示测试之前发生的一个前置条件。如:“Given the user was logged-in” 。
在Gherkin中定义包含When语句的时候,要使用现在时时态,这代表测试执行的时候发生的,如:“When the user cancels the form”
在Gherkin中定义包含Then语句的时候,用情态动词写成期望例如:Then the form should be closed。这强调了我们不是预测SUT将如何表现,而是描述我们对它的期望。
规则4:使用角色名称
使用这些角色名称:如 “Users”、“Admin”、“Guest”,而不是 “I”。这可以增加一个场景的重点,让它专注于某一个角色,以便更容易阅读理解。在后续步骤中,要么重复角色名称,要么使用代词They来代表这类角色。
例如:
When the Admin starts the creation of a user
Then the Admin should be asked to confirm the creation
又如:
When the Admin starts the creation of a user
Then they should be asked to confirm the creation
规则5:使用大写的否定词
否定词会大大改变句子的意思,但很容易被忽视。把否定词写成大写字母,便可以强调它们。
例如:
“The the text “Welcome” should NOT be visible” 或 “The user should NOT exists”
符合受众需求的产出物
作为技术人员,往往具有很强的工程师思维惯性,产出物也是有鲜明的技术标签。所以从业务的视角来看,并不是那么对用户友好。我们定义的第一版步骤库便是如此。比如Flutter项目中所有的对象都可以加上类似于id的key属性,用来查找这个唯一的对象元素,如果在步骤中要用这个属性来寻找对象,那么步骤变成了类似:
When the element with key “userNameTextField” is filled with text “john@gmail.com”
这么定义出来的步骤可能有如下问题:
- 具有很强的编码能力的Dev/QA可能更倾向于直接使用程序语言来书写测试加快执行,那么这套Ghkerin库会被弃置。
- 没有代码仓库访问权限的QA或者BA无法通过访问代码查看具体某个对象的Key是多少,那么这套Gherkin库他/她无法使用。
作为业务人员,更希望在步骤中隐藏所有技术细节,方便使用。所以我们做了如下改进:
- 隐藏所有的Key细节,尽可能使用元素可见的属性,或者目标元素和一个可见元素的相对关系来定位元素,如按钮的文字,下拉选项上面的Label文字,文字输入框的ToolTip,表格的标题等等。
- 如果一个对象没有可见的属性必须使用Key定位的话,我们将驼峰式变量名的Key映射到友好的自然语言功能名称上,同时维护一个Wiki文档,这样业务使用人员可以方便地查找和使用这个步骤。
于是,上面的步骤就变成了:
When the text field “user name” is filled with “john@gmail.com”
动作式Gherkin风格详解
我们使用动作式Gherkin定义了用例,通过总结,有如下经验。动作式Gherkin语句用每一个参数化的步骤描述一个行为,这种风格使得步骤库的体积不必非常大。因此也减少了自动化框架步骤的开发和维护工作量,每一个步骤尽量和公共组件进行互动,也保证了每个步骤的重用性非常之高,所以,一旦需要新测试,用现有的动作式步骤库书写后这个case就可以立即运行了,也不需要实现一个新的步骤。
使用场景:写测试用例
例子:
When the button ‘Create User’ is clicked
And the text field ‘Last name’ is filled with ‘Jim’
And the text field ‘First name’ is filled with ‘Green’
And the button ‘Save’ is clicked
Then the ‘user list’ should contain the text ‘Jim, Green’
规则1:使用单一测试点的Scenario场景
由于动作式Gherkin不可避免要使用更多的步骤,所以动作式Gherkin的测试长度一般都会更长一些,但是这并不代表一个测试可以写很长很大。同样地,一个测试还是需要遵循单一原则,最好覆盖一个测试点,在覆盖这个测试点的过程中,尽可能减少测试步骤让这个测试简短精悍,方便维护。
规则2:使用被动语态
由于动作式Gherkin是对UI对象的操作,为了方便阅读,加强对互动的UI元素的关注所以一般是 "X is clicked"这种被动的语态。
例子:
When the text field ‘Last name’ is filled with ‘CAO’
反例:
When fill ‘CAO’ into the text field ‘Last name’
规则3:使用不同时态
和文档式Gherkin一样,使用不同的时态来区分先决条件、行为、期望。
Given步骤用过去时时态,例如:Given user was logged-in.
When步骤用现在时来描述动作, 例如:When button ‘Login’ is clicked.
Then步骤用情态动词描述期望,Then the text ‘Welcome’ should be visible.
规则4:专注于UI元素
动作式Gherkin步骤专注于和UI界面互动,所以尽可能隐藏用户角色信息,一般来说,在Given步骤中给定了一个用户角色即可,而之后,便不在语句中强调用户角色,把重点放在用户界面元素上,这样可以缩短自然语句中的措辞,突出用户界面,这是动作式Gherkin语句最关注的部分。
比如:
例子:
Given the ‘Admin’ was logged-in
When the button ‘Delete user’ is clicked
反例:
Given the ‘Admin’ was logged-in
When the ‘Admin’ clicks the ‘Delete user’ button
规则5:使用可见的UI属性
UI元素有不同的属性,一些属性是可见的,这样方便用户区别他们,比如一个按钮可以text,class,id来查找到,为了让对象快速被人识别,那么便应该使用人类可见的属性来识别这些自动化UI对象,那么对于这个按钮就应该用按钮的文本来识别,这样便建立了测试语句和软件UI对象视觉上的强有力的联系。
例子:
When the button ‘<button text>’ is clicked
反例:
When the button with id ‘<id>’ is clicked
有时候,一些对象没有可视的属性,有时候一些对象是其他对象的分组,或者其他对象的描述,比如一个区域,一个层,这时候,便可以使用id。此时,便需要开发团队给特定的元素添加id来支持自动化测试。
例子:
Then the widget with the id ‘header’ should contain text ’ today’s announcement’
但是要注意的是,这套步骤库的受众是谁,如果是非技术性用户,那么尽可能隐藏掉id这种技术细节。
例子:
Then the element ‘homepage header’ should contain text ’ today’s announcement’
规则6:前置条件中隐式假设的使用
例子1:
Given ‘admin’ was logged-in
And the dashboard page is visible
例子2:
Given the dashboard page is visible
我们可以假设,当dashboard可见的时候,管理员必须要登录,那么文档式Gherkin使用例2便可以聚焦到dashboard相关的信息了。然而,在使用代码实现步骤的时候,将多个动作聚集到一个步骤的定义中,会大大降低一个步骤的可重用性,一个复杂的动作不能像原子动作那样与其他步骤结合。一旦这些步骤链中任何一个地方改变,那么整个步骤就要修改维护。
但另一方面,如果一个场景可以将一些步骤聚合在一起,那么便可以大大提高这个场景的可读性,忽略无关信息,如:Given owner exists. 这步其实聚合一系列创建用户的动作,一句话就表达了整个意思。所以编写方案时,需要在这两种需求之间找到一个平衡。
统一手工测试和自动化测试
文档式Gherkin和动作式Gherkin都有它们的适用之处,在写软件需求或者测试时候选择最合适的即可。遵守以上法则,会让定义出来的Gherkin语言符合语言习俗,让英语测试工程师更快速地使用这套步骤快速建立文档和用例,也让自动化框架开发工程师更方便地维护和对接步骤库的使用者。
在提供了基于动作式Gherkin的步骤库后,通过不断地反馈和优化,我们隐藏了对象的ID细节,提供了友好的元素定位方式,以及方便记忆的对象命名库,客户的QA终于可以方便地使用我们的步骤库来书写测试用例了。由于Gherkin步骤本身就是以英语自然语言来书写的,所以它也就自然而然可以用来书写手工测试用例了。一套用例,测试工程师可以看着通俗易懂的Gherkin语言来手动执行用例,Flutter上的类Cucumber自动化框架也可以用自动化执行用例出具报告。这样一来,传统的“手动测试维护一套手工用例,自动化测试再维护一套从手工测试转化成的自动化用例”的工作流不再存在。终于可以大大减少用例的维护和执行开销了。
文/Thoughtworks 曹植野
原文链接:如何定义高质量的Gherkin自然语言步骤库-Thoughtworks洞见
相关文章:
如何高效使用Gherkin
背景 时间回到2022年,我参与了一个使用了Flutter技术构建的Web前端项目。在这个项目上,我们小组的目标是实施Flutter前端自动化测试。 彼时,Flutter 2.x刚在Web端发力不久,Flutter Web上的应用和生态才刚刚开始,而在…...

[CKA]考试之调度 pod 到指定节点
由于最新的CKA考试改版,不允许存储书签,本博客致力怎么一步步从官网把答案找到,如何修改把题做对,下面开始我们的 CKA之旅 题目为: Task 创建一个Pod,名字为nginx-kusc00401,镜像地址是nginx…...

git 常用命令有哪些
Git 是我们开发工作中使用频率极高的工具,下面总结下他的基本指令有哪些,顺便温习一下。 前言 一般项目中长存2个分支: 主分支(master) 和开发分支(develop) 项目存在三种短期分支 ࿱…...
算法leetcode|66. 加一(rust重拳出击)
文章目录 66. 加一:样例 1:样例 2:样例 3:提示: 分析:题解:rust:go:c:python:java: 66. 加一: 给定一个由 整数 组成的 非…...
MySQL备份Shell脚本
将此脚本添加到crontab计划中,自动留存最新的两份备份 #!/bin/bash # 数据库配置 DB_HOST"localhost" DB_USER"root" DB_PASS"Sxbdc123!#" DB_NAME"ww"# 备份目录 BACKUP_DIR"/opt/mysqlbak"# 备份文件名称 BA…...

Python批量查字典和爬取双语例句
最近,有网友反映,我的批量查字典工具换到其它的网站就不好用了。对此,我想说的是,互联网包罗万象,网站的各种设置也有所不同,并不是所有的在线字典都可以用Python爬取的。事实上,很多网站为了防…...

uni-app、H5实现瀑布流效果封装,列可以自定义
文章目录 前言一、效果二、使用代码三、核心代码总结前言 最近做项目需要实现uni-app、H5实现瀑布流效果封装,网上搜索有很多的例子,但是代码都是不够完整的,下面来封装一个uni-app、H5都能用的代码。在小程序中,一个个item渲染可能出现问题,也通过加锁来解决问题。 一、…...

vue echart3个饼图
概览:根据UI设计需要做3个饼图且之间有关联,并且处理后端返回的数据。 参考链接: echart 官网的一个案例,3个饼图 实现思路: 根据案例,把数据处理成对应的。 参考代码: 1.处理后端数据&am…...

LEARNING TO EXPLORE USING ACTIVE NEURAL SLAM 论文阅读
论文信息 题目:LEARNING TO EXPLORE USING ACTIVE NEURAL SLAM 作者:Devendra Singh Chaplot, Dhiraj Gandhi 项目地址:https://devendrachaplot.github.io/projects/Neural-SLAM 代码地址:https://github.com/devendrachaplot/N…...

item_search-ks-根据关键词取商品列表
一、接口参数说明: item_search-根据关键词取商品列表,点击更多API调试,请移步注册API账号点击获取测试key和secret 公共参数 请求地址: https://api-gw.onebound.cn/ks/item_search 名称类型必须描述keyString是调用key(http:…...
windows运行WPscan报错:无法打开库libcurl.dll
windows运行WPscan报错:无法打开库libcurl.dll 1.问题背景2.解决方案1.问题背景 在Windows上启动WPScan时: wpscan --url xxx.ru提示如下错误: Could not open library libcurl.dll: �� ������ ��������� ������. . Could not open library libcu...

web前端框架Javascript之JavaScript 异步编程史
早期的 Web 应用中,与后台进行交互时,需要进行 form 表单的提交,然后在页面刷新后给用户反馈结果。在页面刷新过程中,后台会重新返回一段 HTML 代码,这段 HTML 中的大部分内容与之前页面基本相同,这势必造成…...

Java多线程(1)---多线程认识、四种创建方式以及线程状态
目录 前言 一.Java的多线程 1.1多线程的认识 1.2Java多线程的创建方式 1.3Java多线程的生命周期 1.4Java多线程的执行机制 二.创建多线程的四种方式 2.1继承Thread类 ⭐创建线程 ⭐Thread的构造方法和常见属性 2.2.实现Runnable接口 ⭐创建线程 ⭐使用lambda表达…...

搭建Django+pyhon+vue自动化测试平台
Django安装 使用管理员身份运行pycharm使用local 1 pip install django -i https://pypi.tuna.tsinghua.edu.cn/simple 检查django是否安装成功 1 python -m django --version 创建项目 1 1 django-admin startproject test cd 切换至创建的项目中启动django项目…...

CASAIM自动化平面度检测设备3D扫描零部件形位公差尺寸测量
平面度是表面形状的度量,指示沿该表面的所有点是否在同一平面中,当两个表面需要连接在一起形成紧密连接时,平面度检测至关重要。 CASAIM自动化平面度检测设备通过搭载领先的激光三维测头和智能检测软件自动获取零部件高质量测量数据…...
PostgreSql pg_ctl 命令
一、概述 控制 PostgreSQL 服务的工具。 二、语法 --初始化数据库实例 pg_ctl init[db] [-D datadir] [-s] [-o initdb-options]--启动数据库实例 pg_ctl start [-D datadir] [-l filename] [-W] [-t seconds] [-s] [-o options] [-p path] [-c]--停止数据库实例 pg_ctl sto…...
MySQL中的MVCC具体指的是什么?
在MySQL中,MVCC是指多版本并发控制(Multi-Version Concurrency Control)。它是一种用于处理并发读写操作的数据库事务管理技术。 MVCC通过在数据库中维护多个版本的数据来实现并发控制,每个事务在执行期间看到的数据版本是确定性…...

Docker网络模型详解
目录 一、Docker网络基础 1、端口映射 使用-P选项时Docker会随机映射一个端口至容器内部的开放端口 使用docker logs查看Nginx的日志 查看映射的随机端口范围 2、使用-p可以指定要映射到的本地端口。 Local_Port:Container_Port : 端口映射参数中指定了宿主…...

如何打造属于自己的个人IP?
在当今信息爆炸的时代,个人 IP 已经成为人们在网络世界中的独特标签。无论是在职场上、创业中,还是在社交生活中,拥有个人 IP 的人都能脱颖而出,吸引更多的关注和机会。那么,如何打造属于自己的个人 IP 呢?…...

全网最全最细的jmeter接口测试教程以及接口测试流程详解
一、Jmeter简介 Jmeter是由Apache公司开发的一个纯Java的开源项目,即可以用于做接口测试也可以用于做性能测试。 Jmeter具备高移植性,可以实现跨平台运行。 Jmeter可以实现分布式负载。 Jmeter采用多线程,允许通过多个线程并发取样或通过…...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...