行为驱动开发 - 快速指南



行为驱动开发 - 简介

行为驱动开发 (BDD) 是一种软件开发流程,最初起源于测试驱动开发 (TDD)。

根据负责 BDD 演进的 Dan North 的说法,“BDD 是在多个层面上使用示例来创建共享理解并揭示不确定性,以交付有意义的软件。”

BDD 使用示例来说明系统的行为,这些示例以每个人参与开发都能理解的易读语言编写。这些示例包括 -

  • 转换为可执行的规范。

  • 用作验收测试。

BDD – 关键特性

行为驱动开发侧重于 -

  • 提供共享流程和共享工具,促进软件开发人员、业务分析师和利益相关者之间的沟通,以协作进行软件开发,目标是交付具有业务价值的产品。

  • 系统应该做什么,而不是如何实现它。

  • 提供更好的可读性和可见性。

  • 不仅验证软件的工作原理,还验证它是否满足客户的期望。

BDD 的起源

如果缺陷没有在正确的时间被检测到并及时修复,则修复缺陷的成本会成倍增加。请考虑以下示例。

Origin of BDD

这表明,除非正确获取需求,否则在后期阶段因误解需求而导致的缺陷修复成本将会很高。此外,最终产品可能无法满足客户的期望。

当务之急是一种开发方法,该方法 -

  • 基于需求。

  • 在整个开发过程中关注需求。

  • 确保满足需求。

BDD 是一种可以满足上述需求的开发方法。因此,行为驱动开发 -

  • 推导出系统不同预期行为的示例。

  • 能够使用业务领域术语以一种语言编写示例,以确保所有参与开发的人员(包括客户)都能轻松理解。

  • 通过对话不时与客户确认示例。

  • 在整个开发过程中关注客户需求(示例)。

  • 使用示例作为验收测试。

BDD 实践

BDD 的两个主要实践是 -

  • 示例规范 (SbE)

  • 测试驱动开发 (TDD)

示例规范

示例规范 (SbE) 使用对话中的示例来说明业务规则以及要构建的软件的行为。

示例规范使产品负责人、业务分析师、测试人员和开发人员能够消除对业务需求的常见误解。

测试驱动开发

在 BDD 的上下文中,测试驱动开发将示例转换为人类可读的可执行规范。

开发人员使用这些规范作为指南来实现新功能的增量。这导致代码库精简,并提供一套自动化回归测试,从而在软件的整个生命周期内保持较低的维护成本。

敏捷 BDD

在敏捷软件开发中,BDD 方法用于达成对未决规范的共同理解。

在敏捷 BDD 中执行以下步骤 -

  • 开发人员和产品负责人协作在纯文本编辑器中编写未决规范。

  • 产品负责人指定他们期望系统具有的行为。

  • 开发人员

    • 用这些行为细节填充规范。

    • 根据他们对系统的理解提出问题。

  • 考虑当前的系统行为,以查看新功能是否会破坏任何现有功能。

敏捷宣言和 BDD

敏捷宣言声明以下内容 -

我们正在通过实践和帮助他人实践来发现更好的软件开发方法。通过这项工作,我们已经认识到以下价值 -

  • 个体和互动 - 高于流程和工具

  • 可工作的软件 - 高于详尽的文档

  • 客户合作 - 高于合同谈判

  • 响应变化 - 高于遵循计划

也就是说,虽然右侧的项目有价值,但我们更重视左侧的项目。

BDD 与敏捷宣言的对应关系如下 -

敏捷宣言 BDD 对应关系
个体和互动高于流程和工具。 BDD 是关于进行对话的。
可工作的软件高于详尽的文档。 BDD 侧重于简化创建具有业务价值的软件。
客户合作高于合同谈判。 BDD 侧重于基于想法的场景,并在开发过程中与客户进行持续沟通。它不基于任何承诺。
响应变化高于遵循计划。 BDD 侧重于持续沟通和协作,这有助于吸收变化。

BDD - 测试驱动开发

当您查看任何关于行为驱动开发的参考时,您会发现诸如“BDD 派生自 TDD”、“BDD 和 TDD”之类的短语。要了解 BDD 如何产生、为什么说它派生自 TDD 以及 BDD 和 TDD 是什么,您必须了解 TDD。

为什么要测试?

首先,让我们深入了解测试的基本原理。测试的目的是确保构建的系统按预期工作。请考虑以下示例。

Testing

因此,根据经验,我们了解到在引入缺陷时立即发现并修复它将是经济高效的。因此,有必要在开发和测试的每个阶段编写测试用例。这就是我们传统的测试实践所教给我们的,通常称为尽早测试。

Exploratory Testing

这种测试方法称为最后测试方法,因为测试是在一个阶段完成后进行的。

最后测试方法的挑战

在软件开发项目中,最后测试方法已经沿用了相当长一段时间。然而,实际上,由于这种方法需要等到特定阶段完成后才能进行测试,因此常常会被忽视,原因是 -

  • 阶段完成的延迟。

  • 时间安排紧张。

  • 专注于按时交付,跳过测试。

此外,在最后测试方法中,开发人员应该进行的单元测试常常会被跳过。发现的各种原因都基于开发人员的心态 -

  • 他们是开发人员,而不是测试人员。

  • 测试是测试人员的责任。

  • 他们擅长编码,他们的代码不会有缺陷。

这导致 -

  • 影响交付产品的质量。

  • 仅将质量责任归于测试人员。

  • 交付后修复缺陷的成本很高。

  • 无法获得客户满意度,这也意味着失去回头客,从而影响信誉。

这些因素要求转变范式,专注于测试。结果是首要测试方法。

首要测试方法

首要测试方法将自内而外(编写代码然后测试)的开发方式替换为自外而内(编写测试然后编写代码)的开发方式。

此方法已纳入以下软件开发方法(也属于敏捷方法) -

  • 划 (XP)。

  • 发 (TDD)。

在这些方法中,开发人员在编写代码模块的任何一行代码之前,都会设计并编写该代码模块的单元测试。然后,开发人员创建代码模块,目标是通过单元测试。因此,这些方法使用单元测试来驱动开发。

需要注意的基本点是,目标是基于测试的开发。

红-绿-重构循环

测试驱动开发用于开发由单元测试引导的代码。

步骤 1 - 考虑要编写的代码模块。

步骤 2 - 编写测试

步骤 3 - 运行测试。

测试失败,因为代码尚未编写。因此,步骤 2 通常称为编写测试以失败。

步骤 4 - 编写尽可能少的代码以通过测试。

步骤 5 - 运行所有测试以确保它们都仍然通过。单元测试是自动化的,以促进此步骤。

步骤 6 - 重构。

步骤 7 - 对下一个代码模块重复步骤 1 到步骤 6。

每个循环都应该非常短,典型的一个小时应该包含许多循环。

Red Green Refactor Cycle

这也被称为红-绿-重构循环,其中 -

  • 红色 - 编写失败的测试。

  • 绿色 - 编写代码以通过测试。

  • 重构 - 删除重复项并将代码改进到可接受的标准。

TDD 流程步骤

下面说明了 TDD 流程的步骤。

TDD Process Steps

TDD 的优势

测试驱动开发的好处或优势包括 -

  • 开发人员需要首先了解期望的结果是什么以及如何在创建代码之前对其进行测试。

  • 只有当测试通过并且代码经过重构后,某个组件的代码才算完成。这确保了在开发人员转到下一个测试之前进行测试和重构。

  • 由于在每次重构后都会运行单元测试套件,因此每个组件仍然可以工作的反馈是持续的。

  • 单元测试充当始终是最新的动态文档。

  • 如果发现缺陷,开发人员会创建一个测试来揭示该缺陷,然后修改代码以使测试通过并修复缺陷。这减少了调试时间。所有其他测试也会运行,并且当它们通过时,确保现有的功能没有被破坏。

  • 开发人员可以随时做出设计决策并重构,并且测试的运行确保系统仍在工作。这使得软件易于维护。

  • 开发人员有信心进行任何更改,因为如果更改影响任何现有功能,则可以通过运行测试来揭示相同的功能,并且可以立即修复缺陷。

  • 在每次后续测试运行中,所有先前的缺陷修复也将得到验证,并且减少了相同缺陷的重复。

  • 由于大部分测试是在开发过程中完成的,因此交付前的测试时间缩短了。

TDD的缺点

起点是用户故事,描述系统的行为。因此,开发人员经常面临以下问题:

  • 何时测试?

  • 测试什么?

  • 如何知道是否满足规范?

  • 代码是否交付了业务价值?

关于TDD的误解

行业中存在以下误解,需要澄清。

误解 澄清
TDD完全是关于测试和测试自动化。 TDD是一种使用测试优先方法的开发方法。
TDD不涉及任何设计。 TDD包括基于需求的关键分析和设计。设计在开发过程中出现。
TDD仅在单元级别。 TDD可用于集成和系统级别。
TDD不能用于传统的测试项目。 TDD随着极限编程而流行,并被用于其他敏捷方法。但是,它也可以用于传统的测试项目。
TDD是一种工具。

TDD是一种开发方法,每次新的单元测试通过后,都会将其添加到自动化测试套件中,因为每次添加新代码或修改现有代码以及每次重构后都需要运行所有测试。

因此,支持TDD的测试自动化工具促进了这一过程。

TDD意味着将验收测试交给开发人员。 TDD并不意味着将验收测试交给开发人员。

验收TDD

验收测试驱动开发(ATDD)在开发早期创建用户故事期间定义验收标准和验收测试。ATDD侧重于客户、开发人员和测试人员之间的沟通和共同理解。

ATDD中的关键实践如下:

  • 讨论现实世界中的场景,以建立对领域的共同理解。

  • 使用这些场景得出验收标准。

  • 自动化验收测试。

  • 将开发重点放在这些测试上。

  • 使用测试作为实时规范来促进更改。

使用ATDD的好处如下:

  • 需求明确且没有功能差距。

  • 其他人了解开发人员预见的特殊情况。

  • 验收测试指导开发。

Acceptance TDD

TDD与BDD

根据Dan North的说法,程序员在执行测试驱动开发时通常会遇到以下问题:

  • 从哪里开始

  • 测试什么和不测试什么

  • 一次测试多少

  • 如何称呼他们的测试

  • 如何理解测试失败的原因

所有这些问题的解决方案都是行为驱动开发。它从既定的敏捷实践中发展而来,旨在使它们更容易被不熟悉敏捷软件交付的团队所访问和使用。随着时间的推移,BDD已经发展到涵盖敏捷分析和自动化验收测试的更广泛的范围。

TDD和BDD之间的主要区别在于:

  • TDD描述了软件的工作原理。

  • 另一方面,BDD:

    • 描述最终用户如何使用软件。

    • 促进协作和沟通。

    • 强调系统行为的示例。

    • 旨在从示例中获得可执行的规范

BDD - 以BDD方式进行TDD

在TDD中,“验收测试”一词具有误导性。验收测试实际上代表了系统的预期行为。在敏捷实践中,强调整个团队的协作以及与客户和其他利益相关者的互动。这导致了使用每个人都容易理解的术语的必要性。

TDD让你思考所需的行为,因此“行为”一词比“测试”一词更有用。BDD是测试驱动开发,其词汇侧重于行为而不是测试。

用Dan North的话说,“我发现从思考测试到思考行为的转变是如此深刻,以至于我开始将TDD称为BDD或行为驱动开发。”TDD关注某件事如何工作,BDD关注我们为什么要构建它。

BDD回答了开发人员经常面临的以下问题:

问题 答案
从哪里开始? 自顶向下
测试什么? 用户故事
不测试什么? 其他任何事

这些答案导致以下故事框架:

故事框架

作为一个[角色]

我想要[功能]

以便[利益]

这意味着,“当功能被执行时,产生的利益归属于扮演角色的人。”

BDD进一步回答了以下问题:

问题 答案
一次测试多少? 非常少,重点突出
如何称呼他们的测试? 句子模板
如何理解测试失败的原因 文档

这些答案导致以下示例框架:

示例框架

给定一些初始上下文,

事件发生时,

然后确保一些结果。

这意味着,“从初始上下文开始,当发生特定事件时,我们知道应该是什么结果。”

因此,示例显示了系统的预期行为。这些示例用于说明系统的不同场景。

故事和场景

让我们考虑Dan North关于ATM系统的以下示例。

故事

作为一个客户,

我想要从ATM机上取款,

以便我不必在银行排队。

场景

这个故事有两种可能的场景。

场景1 - 账户有余额

给定账户有余额

并且卡有效

并且出纳机里有现金

客户请求现金时

然后确保账户被扣款

并且确保现金被发放

并且确保卡被退回

场景2 - 账户透支超过透支限额

给定账户透支

并且卡有效

客户请求现金时

然后确保显示拒绝消息

并且确保现金未被发放

并且确保卡被退回

这两个场景中的事件相同,但上下文不同。因此,结果也不同。

开发周期

BDD的开发周期采用自顶向下的方法。

步骤1 - 编写一个高级(外部)业务价值示例(使用Cucumber或RSpec/Capybara),使其变为红色。(RSpec在Ruby语言中生成BDD框架)

步骤2 - 为第一步实现编写一个较低级别(内部)RSpec示例,使其变为红色。

步骤3 - 实现通过该较低级别示例所需的最小代码,使其变为绿色。

步骤4 - 编写下一个较低级别RSpec示例,推动通过步骤1,使其变为红色。

步骤5 - 重复步骤3和步骤4,直到步骤1中的高级示例变为绿色。

注意 - 应牢记以下几点:

  • 红/绿状态是权限状态。

  • 当您的低级测试为绿色时,您有权编写新示例或重构现有实现。在重构的上下文中,您不得添加新功能/灵活性。

  • 当您的低级测试为红色时,您只有权编写或更改实现代码以使现有测试变为绿色。您必须抵制编写代码以通过您尚未存在的下一个测试或实现您认为很好的功能(客户不会提出)的冲动。

BDD - 示例规范

根据“基于示例的规范”的作者Gojko Adzic,“基于示例的规范是一组流程模式,可以促进软件产品的变更,以确保有效地交付正确的产品。”

基于示例的规范是一种协作方法,用于定义软件产品的需求和面向业务的功能测试,该方法基于使用现实示例而不是抽象陈述来捕获和说明需求。

基于示例的规范 - 概述

基于示例的规范的目的是专注于优先级排序、可验证的业务需求的开发和交付。虽然基于示例的规范本身的概念相对较新,但它只是对现有实践的重新表述。

它支持一个非常具体、简洁的词汇,称为普遍语言,该语言:

  • 启用可执行的需求。

  • 由团队中的每个人使用。

  • 由跨职能团队创建。

  • 捕捉每个人的理解。

基于示例的规范可以直接用作构建反映业务领域的自动化测试的输入。因此,基于示例的规范的重点在于构建正确的产品和正确地构建产品。

基于示例的规范的目的

基于示例的规范的主要目的是构建正确的产品。它侧重于共享理解,从而建立单一的事实来源。它能够自动化验收标准,以便重点放在缺陷预防而不是缺陷检测上。它还提倡尽早测试以尽早发现缺陷。

SbE的使用

基于示例的规范用于说明描述业务价值的预期系统行为。插图是通过具体和现实生活中的例子来实现的。这些示例用于创建可执行的需求,这些需求:

  • 无需翻译即可测试。

  • 捕获在实时文档中。

以下是我们使用示例来描述特定规范的原因:

  • 它们更容易理解。

  • 它们更难误解。

SbE的优势

使用基于示例的规范的优势在于:

  • 质量提高

  • 减少浪费

  • 降低生产缺陷的风险

  • 重点工作

  • 可以更安全地进行更改

  • 提高业务参与度

SbE的应用

基于示例的规范应用于:

  • 复杂的业务或复杂的组织。

  • 不适用于纯粹的技术问题。

  • 不适用于以UI为中心的软件产品。

  • 也可应用于遗留系统。

SbE和验收测试

基于示例的规范在验收测试方面的优势在于:

  • 一个插图同时用于详细需求和测试

  • 项目的进度以验收测试为依据:

    • 每个测试都是为了测试一种行为。

    • 测试要么通过行为,要么不通过。

    • 测试通过表示特定行为已完成。

    • 如果一个需要完成100种行为的项目完成了60种行为,那么它就完成了60%。

  • 测试人员从修复缺陷转向预防缺陷,并为解决方案的设计做出贡献。

  • 自动化允许即时了解需求变更对解决方案的影响。

基于示例的规范 - 对不同角色的意义

基于示例的规范的目的是促进整个团队(包括客户)在整个项目中的协作,以交付业务价值。为了更好地理解,每个人都使用相同的词汇。

角色 SbE的使用
业务分析师
  • 需求明确且没有功能差距。

  • 开发人员,实际上阅读规范。

开发人员

  • 开发人员能够更好地理解正在开发的内容。

  • 通过统计已正确开发的规格,可以更好地跟踪开发进度。

测试人员
  • 测试人员能够更好地理解正在测试的内容。

  • 测试人员从一开始就参与其中,并在设计中发挥作用。

  • 测试人员致力于缺陷预防,而不是缺陷检测。

所有人
  • 通过从一开始就识别错误,可以节省时间。

  • 从一开始就生产出高质量的产品。

SbE – 一套流程模式

正如我们在本章开头所看到的,基于示例的规范 (Specification by Example) 被定义为一套流程模式,这些模式促进软件产品的变更,以确保高效地交付正确的产品。

这些流程模式包括:

  • 协作规范

  • 使用示例说明规范

  • 细化规范

  • 自动化示例

  • 频繁验证

  • 活文档

协作规范

协作规范的目标是:

  • 使团队中的各个角色拥有共同的理解和共享的词汇。

  • 让每个人都参与到项目中,以便他们能够贡献他们对功能的不同视角。

  • 确保功能的共享沟通和所有权。

这些目标在规范研讨会(也称为“三位一体”会议)中实现。“三位一体”是指业务分析师 (BA)、质量保证 (QA) 和开发人员。尽管项目中还有其他角色,但这三位将负责并对从功能定义到交付负责。

在会议期间:

  • 业务分析师 (BA) 展示新功能的需求和测试。

  • 三位一体 (BA、开发人员和 QA) 讨论新功能并审查规范。

  • QA 和开发人员还识别缺失的需求。

  • 三位一体

    • 利用共享模型使用普遍语言。

    • 使用领域词汇(如果需要,维护词汇表)。

    • 寻找差异和冲突。

  • 此时不要跳到实现细节。

  • 就功能是否已充分指定达成共识。

  • 共享的需求和测试所有权有助于提高规范的质量。

  • 需求以场景的形式呈现,这些场景提供了明确、无歧义的需求。场景是从用户角度来看系统行为的一个示例。

使用示例说明规范

场景使用 Given-When-Then 结构进行指定,以创建可测试的规范:

给定 <某些前提条件>

并且 <其他前提条件> 可选

<某个动作/触发器发生>

那么 <某些后置条件>

并且 <其他后置条件> 可选

此规范是系统行为的一个示例。它也代表了系统的验收标准。

团队讨论这些示例,并纳入反馈,直到达成一致意见,认为这些示例涵盖了功能的预期行为。这确保了良好的测试覆盖率。

细化规范

要细化规范,

  • 编写示例时要准确。如果某个示例变得复杂,则将其拆分为更简单的示例。

  • 专注于业务视角,避免技术细节。

  • 考虑正负条件。

  • 遵守特定领域的词汇。

  • 与客户讨论示例。

    • 选择对话来完成此操作。

    • 仅考虑客户感兴趣的示例。这使得仅生成所需的代码成为可能,并避免覆盖可能不需要的每种可能的组合。

  • 为了确保场景通过,该场景的所有测试用例都必须通过。因此,增强规范以使其可测试。测试用例可以包括各种范围和数据值(边界和角落情况),以及导致数据变化的不同业务规则。

  • 指定其他业务规则,例如复杂计算、数据操作/转换等。

  • 将非功能场景(例如性能、负载、可用性等)包含为基于示例的规范。

自动化示例

自动化层需要保持非常简单——只需将规范连接到被测系统即可。您可以为此使用工具。

使用领域特定语言 (DSL) 执行测试自动化,并显示输入和输出之间的清晰连接。专注于规范,而不是脚本。确保测试精确、易于理解且可测试。

频繁验证

在每次更改(添加/修改)时将示例验证包含在您的开发管道中。有许多技术和工具可以(并且应该)被采用来帮助确保产品的质量。它们围绕三个关键原则展开——**尽早测试、测试好**和**经常测试**。

频繁执行测试,以便您可以识别薄弱环节。代表行为的示例有助于跟踪进度,并且只有在相应的测试通过后,才认为行为已完成。

活文档

保持规范尽可能简单和简短。组织规范并在工作进行时对其进行演变。使团队中的所有人员都能访问文档。

基于示例的规范流程步骤

插图显示了基于示例的规范中的流程步骤。

Living Documentation

反模式

反模式是软件开发中某些被认为是不良编程实践的模式。与设计模式相反,设计模式是针对常见问题的常见方法,这些方法已被形式化并且通常被认为是良好的开发实践,而反模式则相反,并且是不希望的。

反模式会导致各种问题。

反模式 问题
缺乏协作
  • 许多假设

  • 构建错误的东西

  • 测试错误的东西

  • 不知道代码何时完成

不知道代码何时完成
  • 难以维护测试

  • 难以理解规范

  • 业务代表失去兴趣

过于详细或过于以 UI 为中心的示例
  • 难以维护测试

  • 难以理解规范

  • 业务代表失去兴趣

低估所需的工作量
  • 团队认为他们失败了,并在早期感到失望

解决问题的方案 - 质量

可以通过关注反模式来确保质量。为了最大程度地减少反模式造成的问题,您应该:

  • 一起使用示例进行规范。

  • 清理和改进示例。

  • 编写满足示例的代码。

  • 自动化示例并部署。

  • 对每个用户故事重复此方法。

解决由于反模式造成的问题意味着遵守:

  • 协作。

  • 专注于“什么”。

  • 专注于业务。

  • 做好准备。

让我们了解上述内容的含义。

协作

在协作中:

  • 业务人员、开发人员和测试人员从各自的角度提供输入。

  • 自动化的示例证明团队构建了正确的东西。

  • 该流程比测试本身更有价值。

专注于“什么”

您必须专注于“什么”这个问题。在专注于“什么”时:

  • 不要试图涵盖所有可能的情况。

  • 不要忘记使用不同类型的测试。

  • 保持示例尽可能简单。

  • 示例应该易于系统用户理解。

  • 工具不应该在研讨会中发挥重要作用。

专注于业务

要专注于业务:

  • 将规范保持在业务意图层面。

  • 让业务参与创建和审查规范。

  • 将所有细节隐藏在自动化层中。

做好准备

为以下情况做好准备:

  • 即使团队实践发生变化,好处也不会立即显现。

  • 引入 SbE 具有挑战性。

  • 需要时间和投资。

  • 自动化测试并非免费。

工具

基于示例的规范不需要使用工具,尽管在实践中可以使用多种工具。即使不使用工具,也有一些案例成功地遵循了基于示例的规范。

以下工具支持基于示例的规范:

  • Cucumber

  • SpecFlow

  • Fitnesse

  • Jbehave

  • Concordion

  • Behat

  • Jasmine

  • Relish

  • Speclog

行为驱动开发 - 工具

开发团队经常误以为 BDD 是一个工具框架。实际上,BDD 是一种开发方法,而不是工具框架。但是,与其他开发方法一样,BDD 也有一些工具。

有几种 BDD 工具用于不同的平台和编程语言。它们包括:

  • Cucumber(Ruby 框架)

  • SpecFlow(.NET 框架)

  • Behave(Python 框架)

  • JBehave(Java 框架)

  • JBehave Web(具有 Selenium 集成的 Java 框架)

  • Lettuce(Python 框架)

  • Concordion(Java 框架)

  • Behat(PHP 框架)

  • Kahlan(PHP 框架)

  • DaSpec(JavaScript 框架)

  • Jasmine(JavaScript 框架)

  • Cucumber-js(JavaScript 框架)

  • Squish GUI Tester(用于 JavaScript、Python、Perl、Ruby 和 Tcl 的 BDD GUI 测试工具)

  • Spock(Groovy 框架)

  • Yadda(对 Jasmine(JavaScript 框架)等框架提供 Gherkin 语言支持)

Cucumber

Cucumber 是一款用于可执行规范的免费工具,在全球范围内使用。Cucumber 允许软件开发团队用纯文本描述软件的行为。文本以业务可读的、特定于领域的语言编写,并作为文档、自动化测试和开发辅助工具,全部集成到一种格式中。您可以使用超过 40 种不同的口语(英语、中文等)与 Cucumber 互动。

Cucumber – 主要功能

Cucumber 的主要功能如下:

  • Cucumber 可用于可执行规范、测试自动化和活文档。

  • Cucumber 可与 Ruby、Java、NET、Flex 或用任何语言编写的 Web 应用程序配合使用。

  • Cucumber 支持更简洁的表格测试——类似于 FIT 的功能。

  • Cucumber 通过将需求、自动化测试和文档融合到一个连贯的整体中彻底改变了软件开发生命周期:可执行的纯文本规范来验证软件。

SpecFlow

SpecFlow 是一个用于 .NET 平台的 BDD 工具。SpecFlow 是一个开源项目。源代码托管在 GitHub 上。

SpecFlow 使用 Gherkin 语法来描述特性。Gherkin 格式由 Cucumber 引入,也用于其他工具。Gherkin 语言作为一个项目在 GitHub 上维护 - https://github.com/cucumber/gherkin

Behave

Behave 用于 Python 框架。

  • Behave 使用三种类型的文件,存储在名为“features”的目录中 -

    • 包含行为场景的特性文件。

    • “steps”目录,包含场景的 Python 步骤实现。

    • 可选地,一些环境控制(在步骤、场景、特性或整个流程之前和之后运行的代码)。

  • Behave 特性使用 Gherkin(带有一些修改)编写,并命名为“name.feature”。

  • 附加到特性和场景的标签可以通过传递给它们的“feature”或“scenario”对象在环境函数中获得。在这些对象上,有一个名为“tags”的属性,它是一个附加的标签名称列表,按照在特性文件中找到的顺序排列。

  • 对 Gherkin 标准的修改 -

    • Behave 可以解析标准的 Gherkin 文件,并扩展 Gherkin 以允许使用小写步骤关键字,因为这些关键字有时可以使特性规范更易读。

Lettuce

Lettuce 是一个基于 Cucumber 的非常简单的 BDD 工具。它可以将纯文本功能描述作为 Python 项目的自动化测试执行。Lettuce 旨在解决 BDD 中最常见的任务。

Concordion

Concordion 是一个用于 Java 框架的自动化示例规范的开源工具。

虽然核心功能很简单,但强大的扩展框架 API允许您添加功能,例如使用 Excel 电子表格作为规范,向输出添加屏幕截图,显示日志信息等。

Concordion 允许您使用段落、表格和正确的标点符号以普通语言编写规范,并且不需要使用 Given/When/Then 结构化语言。

Concordion 已移植到其他语言,包括 -

  • C# (Concordion.NET)

  • Python (PyConcordion)

  • Ruby (Ruby-Concordion)

行为驱动开发 - Cucumber

Cucumber 是一个支持可执行规范、测试自动化和动态文档的工具。

行为驱动开发扩展了示例规范。它还规范了测试驱动开发的最佳实践,特别是从外到内的视角。开发工作基于可执行规范。

可执行规范的主要特征如下 -

  • 可执行规范是 -

    • 源自代表系统行为的示例。

    • 由所有参与开发的人员(包括业务和利益相关者)共同编写。

    • 基于验收标准。

  • 基于可执行规范的验收测试是自动化的。

  • 使用共享的、无处不在的语言来编写可执行规范和自动化测试,以便 -

    • 在整个开发过程中使用特定领域的术语。

    • 每个人,包括客户和利益相关者,都以相同的方式讨论系统、其需求和其实现。

    • 在需求、设计文档、代码、测试等中使用相同的术语来讨论系统。

    • 任何人都可以阅读和理解需求以及如何生成更多需求。

    • 可以轻松地适应更改。

    • 维护动态文档。

Cucumber 有助于此过程,因为它将可执行规范与系统的实际代码和自动化的验收测试联系起来。

它实现此目的的方式实际上旨在让客户和开发人员协同工作。当验收测试通过时,这意味着它所代表的系统行为规范已正确实现。

典型的 Cucumber 验收测试

考虑以下示例。

特性 - 注册

  • 注册应该快速且友好。

  • 场景 - 成功注册

    • 用户应该收到一封确认电子邮件并获得个性化的问候。

    • 假设我选择了注册。

    • 我使用有效信息注册。

    • 那么我应该收到一封确认邮件。

    • 并且我应该看到一条个性化的问候消息。

从这个例子中,我们可以看到 -

  • 验收测试指的是特性

  • 特性由场景解释。

  • 场景由步骤组成。

规范以自然语言写在纯文本文件中,但它是可执行的。

Cucumber 的工作原理

Cucumber 是一个命令行工具,它处理包含特性的文本文件,查找可以针对您的系统执行的场景。让我们了解 Cucumber 的工作原理。

  • 它利用了一些关于文件如何命名以及它们位于何处的约定(各个文件夹),以便于入门。

  • Cucumber 允许您将规范、自动化测试和文档保存在同一位置。

  • 每个场景都是一系列步骤,描述了场景的前置条件、操作和后置条件;如果每个步骤都执行且没有任何错误,则该场景将标记为通过。

  • 在运行结束时,Cucumber 将报告有多少场景通过。

  • 如果某些内容失败,它将提供有关失败内容的信息,以便开发人员可以继续进行。

在 Cucumber 中,特性场景和步骤是用一种称为Gherkin的语言编写的。

Gherkin 是纯文本英语(或 60 多种其他语言之一),具有一定的结构。Gherkin 容易学习,其结构允许您以简洁的方式编写示例。

  • Cucumber 执行包含用 Gherkin 编写的可执行规范的文件。

  • Cucumber 需要步骤定义将纯文本 Gherkin 步骤转换为将与系统交互的操作。

  • 当 Cucumber 执行场景中的一个步骤时,它将查找要执行的匹配步骤定义。

  • 步骤定义是一小段代码,并附加了一个模式。

  • 该模式用于将步骤定义链接到所有匹配的步骤,并且代码是 Cucumber 在看到 Gherkin 步骤时将执行的内容。

  • 每个步骤都伴有一个步骤定义。

  • 大多数步骤将收集输入,然后委托给特定于您的应用程序领域的框架,以便对您的框架进行调用。

Cucumber 支持十多种不同的软件平台。您可以选择适合您的 Cucumber 实现。每个 Cucumber 实现都提供相同的整体功能,并且它们也有自己的安装过程和特定于平台的功能。

映射步骤和步骤定义

Cucumber 的关键在于步骤和步骤定义之间的映射。

Mapping Steps

Cucumber 实现

以下是 Cucumber 的实现。

框架集成

以下是框架实现。

行为驱动开发 - Gherkin

Gherkin 是一种用于编写特性、场景和步骤的语言。Gherkin 的目的是帮助我们编写具体的需求。

要理解我们所说的具体需求是什么,请考虑以下示例 -

应阻止客户输入无效的信用卡详细信息。

对比

如果客户输入的信用卡号码长度不正好为 16 位,当他们尝试提交表单时,应重新显示该表单,并显示一条错误消息,告知他们正确的位数。

后者没有歧义,避免了错误,并且更易于测试。

Gherkin 旨在创建更具体的需求。在 Gherkin 中,上述示例如下所示 -

特性

输入无效信用卡详细信息时的反馈 特性定义

在用户测试中,我们看到许多人犯了错误 文档

背景适用于以下所有场景

假设我选择了一件要购买的商品,

并且我即将输入我的信用卡号码

场景 - 信用卡号码过短场景定义

我输入的卡号少于 16 位

并且所有其他详细信息都正确

并且我提交表单步骤

那么应重新显示表单

并且我应该看到一条消息,告知我正确的位数

Gherkin 格式和语法

Gherkin 文件是纯文本文件,扩展名为 .feature。每行非空行都必须以 Gherkin 关键字开头,后跟任意文本。关键字为 -

  • 特性

  • 场景

  • Given、When、Then、And、But(步骤)

  • 背景

  • 场景大纲

  • 示例

  • """(文档字符串)

  • |(数据表)

  • @(标签)

  • #(注释)

  • *

特性

特性关键字用于描述软件特性,以及对相关场景进行分组。特性有三个基本要素 -

  • 关键字 – 特性。

  • 特性的名称,在与特性关键字相同的行上提供。

  • 可选的(但强烈建议)描述,可以跨越多行,即包含特性关键字的行与以场景、背景或场景大纲开头的行之间的所有文本。

除了名称和描述之外,特性(Feature)还包含一系列场景或场景大纲,以及可选的背景。

通常情况下,.feature 文件的命名方式是采用特性名称,将其转换为小写,并用下划线替换空格。例如:

feedback_when_entering_invalid_credit_card_details.feature

为了在您的系统中识别特性,您可以使用所谓的“特性注入模板”。

为了 <实现某个目标> 作为 <用户类型> 我想要 <一个特性>

描述

Gherkin 文档的某些部分不必以关键字开头。

在特性、场景、场景大纲或示例后面的行中,您可以编写任何内容,只要没有以关键字开头即可。这就是包含描述的方式。

场景

为了表达您系统的行为,您需要为每个特性附加一个或多个场景。通常情况下,每个特性会有 5 到 20 个场景来完整地指定围绕该特性的所有行为。

场景遵循以下模式:

  • 描述初始上下文

  • 描述一个事件

  • 描述预期结果

我们从上下文开始,描述一个动作,并检查结果。这通过步骤来完成。Gherkin 提供三个关键字来描述每个上下文、动作和结果作为步骤。

  • Given - 建立上下文

  • When - 执行动作

  • Then - 检查结果

这些关键字提高了场景的可读性。

示例

Scenario - 从账户中取款。

  • Given 我的账户中有 100 美元。

  • When 我请求取款 20 美元。

  • Then 应该发出 20 美元的钞票。

如果在彼此下方有多个 GivenWhen 步骤,您可以使用 AndBut。它们允许您详细指定场景。

示例

Scenario - 使用被盗卡尝试取款。

  • Given 我的账户中有 100 美元。

  • But 我的卡无效。

  • When 我请求取款 50 美元。

  • Then 我的卡不应该被退回。

  • And 我应该被告知联系银行。

在创建场景时,请记住“每个场景都必须有意义,并且能够独立于任何其他场景执行”。这意味着:

  • 您不能让一个场景的成功条件取决于另一个场景之前是否执行过。

  • 每个场景都会创建其特定的上下文,执行一项操作,并测试结果。

这样的场景提供了以下好处:

  • 测试将更简单,更容易理解。

  • 您可以只运行一部分场景,而无需担心测试集被破坏。

  • 根据您的系统,您可能能够并行运行测试,从而减少执行所有测试所需的时间。

场景大纲

如果您必须编写具有多个输入或输出的场景,您最终可能会创建多个仅值不同的场景。解决方案是使用场景大纲。要编写场景大纲,

  • 场景大纲步骤中的变量用 < 和 > 标记。

  • 变量的各种值作为示例在表格中给出。

示例

假设您正在为计算器上的两个数字相加编写一个特性。

Feature - 加法。

Scenario Outline: Add two numbers.
Given the input "<input>"
When the calculator is run
Then the output should be <output>"
Examples
| input    | output |
| 2+2      | 4      | 
| 98+1     | 99     |
| 255+390  | 645    |

场景大纲部分之后总是跟着一个或多个示例部分,这些示例部分是表格的容器。该表格必须有一个标题行,对应于场景大纲步骤中的变量。下面的每一行都将创建一个新的场景,并填充变量值。

行为驱动开发 - SpecFlow

SpecFlow 是一个开源项目。源代码托管在 GitHub 上。SpecFlow 用于存储应用程序中特性(用例、用户故事)验收标准的特性文件使用 Gherkin 语法定义。

Gherkin 格式由 Cucumber 引入,也由其他工具使用。Gherkin 语言作为 GitHub 上的一个项目维护 - https://github.com/cucumber/gherkin

特性元素和 SpecFlow

特性元素的关键特性是:

  • 特性元素为特性文件提供标题。特性元素包含应用程序中对应特性的名称和高级描述。

    • SpecFlow 为特性元素生成一个单元测试类,类名派生自特性的名称。

    • SpecFlow 从表示验收标准的场景生成可执行的单元测试。

  • 一个特性文件可以包含多个场景,用于描述特性的验收测试。

    • 场景具有名称,可以包含多个场景步骤。

    • SpecFlow 为每个场景生成一个单元测试方法,方法名派生自场景的名称。

多个场景步骤

场景可以有多个场景步骤。有三种类型的步骤定义了先决条件、操作或验证步骤,这些步骤构成了验收测试。

  • 不同的步骤类型分别以 Given、WhenThen 关键字开头,同一类型的后续步骤可以使用 AndBut 关键字链接。

  • Gherkin 语法允许这三种步骤类型的任意组合,但场景通常具有 Given、WhenThen 语句的不同块。

  • 场景步骤使用文本定义,并且可以具有名为 DataTable 的附加表格或名为 DocString 的多行文本参数。

  • 场景步骤是执行任何自定义代码来自动化应用程序的主要方式。

  • SpecFlow 为每个场景步骤在单元测试方法中生成一个调用。该调用由 SpecFlow 运行时执行,该运行时将执行与场景步骤匹配的步骤定义。

  • 匹配是在运行时完成的,因此即使尚未实现绑定,也可以编译和执行生成的测试。

  • 您可以在场景步骤中包含表格和多行参数。这些由步骤定义使用,并且作为附加的表格或字符串参数传递。

标签

标签是可以分配给特性和场景的标记。将标签分配给特性等同于将标签分配给特性文件中的所有场景。以 @ 开头的标签名称表示标签。

  • 如果单元测试框架支持,SpecFlow 会从标签生成类别。

  • 生成的类别名称与标签名称相同,但没有前导 @。

  • 您可以使用这些单元测试类别过滤和分组要执行的测试。例如,您可以将关键测试标记为 @important,然后更频繁地执行这些测试。

背景元素

背景语言元素允许为特性文件中的所有场景指定一个通用先决条件。

  • 文件的背景部分可以包含一个或多个场景步骤,这些步骤在任何其他场景步骤之前执行。

  • SpecFlow 从背景元素生成一个方法,该方法从为场景生成的所有单元测试中调用。

场景大纲

场景大纲可用于定义数据驱动的验收测试。场景大纲始终由场景模板规范(使用 <占位符> 语法的带数据占位符的场景)和一组提供占位符值的示例组成。

  • 如果单元测试框架支持,SpecFlow 会从场景大纲生成基于行的测试。

  • 否则,它会为场景大纲生成一个参数化的单元测试逻辑方法,并为每个示例集生成一个单独的单元测试方法。

  • 为了获得更好的可追溯性,生成的单元测试方法名称派生自场景大纲标题和示例的第一个值(示例表格的第一列)。

  • 因此,最好选择一个唯一且描述性的参数作为示例集的第一列。

  • 由于 Gherkin 语法确实要求所有示例列在场景大纲中具有匹配的占位符,因此您甚至可以在示例集中引入任意列,用于以更具可读性的方式命名测试。

  • SpecFlow 在匹配步骤绑定之前,将占位符替换作为单独的阶段执行。

  • 因此,步骤绑定中的实现和参数独立于它们是通过直接场景还是场景大纲执行。

  • 这允许您以后在验收测试中指定更多示例,而无需更改步骤绑定。

注释

您可以通过在行的开头使用 # 在任何位置向特性文件添加注释行。但是请注意,规范中的注释可能是验收标准指定错误的标志。SpecFlow 会忽略注释行。

广告