Behaviour-Driven Development for your team

新工作 4 个月以来接触 E2E 测试与 BDD 的心得

前言

记录 4 个月来写测试产生的疑问与解答、个人经历、测试的要点与技巧这类语法之上更高层次的问题,以及让我的团队对导入 BDD 有更大的信心与认知。

近期加入新工作期间内除了前端开发也兼任测试,撰写了近百个 E2E 测试,打算通过边撰写本篇文章边查阅文档巩固知识,同时也记录下来方便团队成员能够快速上手新建立的 Cypress E2E 测试项目,并且最终期望能够尝试实验导入 BDD 流程到团队开发流程当中。

第一次与 BDD 以及 E2E 测试见面

会接触 BDD 是因为旧有项目有在尝试导入该开发流程,不过随着人员更动旧有的项目也逐渐弃用,而加入团队第一件事就是建立新的 E2E 测试项目巩固陈旧代码并且加减摸索期望导入 BDD 流程「增进开发产品的效率」与「提高对产品修改时的信心」。

前几个月都是以「通过熟悉现有产品代码与行为」作为出发点开始,过程中顺便翻阅文档边学边写测试,在旧有的项目中则是使用了 Cypress🔗Cucumber🔗

E2E?Cypress?BDD?Cucumber?这些名词对当时的我来说都是陌生的,只想赶快项目快速建起来再慢慢补足理论知识,现在要用一句话总结我会这样描述它们:

  • End-To-End 测试:通过模拟真实用户在应用程序中的操作,以达成验证产品功能与可靠性的目的。
  • Cypress:主要用于 E2E 测试的 JavaScript 框架,用于撰写测试代码自动化模拟用户操作。
  • Behaviour-Driven Development 行为驱动开发:一种着重于系统期望的行为的软件开发模式,通过手段像是:鼓励商业团队与开发团队间相互合作、组织商业可读的规格文件、将规格文件与测试代码连动自动化验证行为是否符合预期……等,来达成目的。
  • Cucumber:用于将商业可读的规格文件(Gherkin 语法)转换成可执行的测试代码的 BDD 工具。

团队合作

既然商业团队与开发团队都被鼓励参与到 BDD 的流程当中,而 BDD 又将测试作为软件开发其中不可分割的验证手段,那么了解一些好测试的基本原则对各方都是有帮助的!如果你认同从产品行为去定义开发产品,以下几个手段与原则或许有帮助:

鼓励商业团队与开发团队合作

最常见的手段就是「三个朋友 Three Amigos」会议,通过聚集 3 方不同视角的人员一同讨论(重点是不同视角不是人数)

  • 💹 商业人员 - 期望解决什么问题?
  • 👨🏻‍💻 开发人员 - 如何构建解方解决问题?
  • 🧪 测试人员 - 有没有可能发生 x 状况?

在商业团队定义出产品的需求时可以依照以下的模板来叙述:身为 (某种用户),需要使用 (某种功能),以便达成 (某种收益)。以下是 Cucumber School 的 BDD 教学范例🔗

身为 (销售助理) 需要使用 (退款功能) 以便达成 (满足用户的法定权力)

虽然范例中开发者的并没有太多发言,不过光是参与这场会议可以让相关人士对需求的前因后果有更深入的了解,并且通常在详细讨论实践细节时开发者更能窥见技术上的限制并与团队一同讨论出可行的解方。

1. 💹:任何产品只要用户还留有发票就可以在 14 天之内退款。
2. 👨🏻‍💻:能提供一个实际案例吗?
3. 💹:A 用户购买了一个商品,但事后不太喜欢产品的颜色,所以她向销售助理申请退款。
4. 🧪:所以我们期望达成什么?有什么是可以测试的?
5. 💹:我们需要增加该产品数量,因为用户已经把卖出去的产品退回来了。
6. 🧪:好的,还有吗?
7. 💹:我想就这样?
8. 🧪:如果用户没有发票呢?销售助理应该直接拒绝请求吗?
9. 💹:因为管理者可以处理任何退款,只需要有任何购买证明……或许 A 用户可以用银行账单来证明她的购买记录。但销售助理没有权限,所以他应该与他的上级沟通。
10. 🧪:好,所以有两种规则,一种是用户有发票,另一种是用户没有发票,而退款时没有发票则需要上级授权,对吧?
11. 💹:对,我想就这样。
12. 👨🏻‍💻:是不是用户应该得到退款?
13. 🧪:以及……是不是系统需要通知仓库回收退款产品?
14. 💹:好主意!我想这次的故事讨论就到这边。

在会议结束后,通过讨论的内容编写产品的行为文件须特别留意。

通常由单一角度像是单独由商业团队开出需求文件时,会出现许多偏差,撰写出来的文件并不代表所有人的共识与理解,这些偏差可能会导致难以开发或自动化测试,而开发者为了能够自动化这些场景着手编写文件又会导致特殊的语法(如 Gherkin)成为开发团队「使用的东西」,并且可能误解真实意图产出不正确的文件。

最好的做法可以是商业与开发双方都要保持共识,通过「行为描述定义出活文件」并通过双方共同审查维护。文件可以依照团队偏好规则撰写,最好团队中任何人都能阅读理解、自动化测试。

文件编写

好测试的 3A 原则

任何测试的结构借鉴 Filip Hric🔗 所提到的 3A 原则其实就是三个简单的步骤:

安排 - 行动 - 断言
  1. 安排 (Arrange):准备测试的前置环境 - 已过去的事
  2. 行动 (Act):执行测试 - 测试要发生的事
  3. 断言 (Assert):验证测试的结果 - 未来的事

用一致的语言来描述产品的行为

为了避免沟通上的偏差,通常会导入某种制式化的语言来描述产品的行为,像是 Gherkin 语法,这种语法的好处是可以让商业团队与开发团队都能够用相同的语言来描述产品的行为,并且通过工具将这些语句对应测试代码转换成可执行测试的活文件。

Given 到达登入页面
When 登入账号密码
Then 成功登入

以 Gherkin 语法为例,看似有许多关键字要学习,实际上所有关键字仍依照测试 3A 规则,围绕在 GivenWhenThen 为这三种关键字功能做延伸,可以参考官方 Gherkin 文件🔗或者是这份超浓缩懒人包🔗,此外 Cucumber 的出发点是编写商业可读的活文件,可以通过在地化 Gherkin 语法🔗 确保每个团队成员更用最有效率的方式快速进入状况沟通与记录产品文件。

使用 BDD 并不意味着一定要使用 Cucumber 的 Gherkin 语法,但它是一个十分有效的工具用于实践 BDD。

过多的实践细节

着重在软件的目标而不是机制 表达规格从问题出发而不是解方出发

操作程序会有许多与 UI 相关的互动细节,像是:「点击特定链接」、「寻找特定组件」,这些描述是用户期望的行为而是极为脆弱易变的实践细节,所以在撰写文件时应该避免这些细节,而是专注于用户期望的行为。以下是实际案例:

@声明式(Good)
场景: 验证登入
假如 无登入用户到达登入页
输入登入资讯并登入
那么 显示登入成功讯息
@命令式(Bad)
场景: 验证登入
假如 无登入用户到达登入页
输入用户名
输入密码
点击登入按钮
那么 显示登入成功讯息

在文件中避免描述实践细节不是偷懒!也不要感觉「既然都来了所以就都写清楚」,假如未来登入的 UI 或资料有任何变动,那么文件也须要修改。让实践的细节撰写在测试当中,文件只描述意图即可。

  1. 文件着重在意图而非实践细节
  2. 意图相较于实践细节更不常更动,也更能采纳实践上的变化
  3. 去除操作细节的文件将更好阅读 (必须让文件前置知识要求或阅读成本降至越低越好)

这个问题时常发生在你是撰写情境文件,同时又是提出解决方案或实践测试功能的人,需要留意文件的本质在描述产品的行为而非实践细节,这样才能确保文件的可读性与可维护性。

结语

软件的开发模式是一个比较抽象广大的主题,随时欢迎联系与我讨论。这篇文章借由我近期的经历与资料收集来总结出一些我认为重要的观念与团队讨论,并期望通过这些观念来帮助所有产品团队更有效率的开发产品。预期有多的东西要补充会再持续更新。

怎么样才算是 BDD?

「怎么样才算是 BDD?」这个问题反复的出现在脑海中,用了 Cucumber 就算是?还是因为测试使用 Given-When-Then 语句做分类?还是跨领域讨论一起编写文件才算?最终我统整出了面对不同人的解释:

  • 开发者
    1. 开发者开始替程序撰写测试
    2. 开发者发现随着测试增加对程序的信心也随之提高
    3. 开发者发现先写测试再开发能够帮助他们专注在必要编写的程序上
    4. 开发者发现回顾一些旧代码时测试能够作为文档协助理解程序的行为
    5. 开发者接受测试可以作为开发文档
    6. 开发者发现 TDD 实际上是在定义行为而非测试
    7. 行为是系统组件之间的交互,因此使用模拟是高级 TDD 的基础。
    8. *我个人总结是:BDD 是 TDD 的团队合作进阶版
  • 普通人
    1. 鼓励商业团队与开发团队间相互合作
    2. 组织商业可读的规格文件
    3. 将规格文件与测试代码连动自动化验证行为是否符合预期

延伸阅读