《Cucumber:行为驱动开发指南》——6.2 同心协力

6.2 同心协力

6.2.1 偶然细节
考虑下面这个为在线邮件客户端编写的场景:

Scenario: Check inbox
 Given a User "Dave" with password "password"
 And a User "Sue" with password "secret"
 And an email to "Dave" from "Sue"
 When I sign in as "Dave" with password "password"
 Then I should see 1 email from "Sue" in my inbox

这个场景中有很多细节:有主要角色Dave的用户名和密码,还有另一个用户Sue的用户名和密码。用户名非常有用,因为它们有助于场景故事的描述,而密码就是噪音了:用户密码与被测内容毫无关系,事实上却让测试更难读懂。比如,Sue的密码跟Dave不同。阅读场景的时候,你会疑惑这是否重要,场景的主要目的是验证Dave可以看Sue的邮件,你的注意力却被分散到别处去了。

像密码这种在场景中提及但实际上与场景的目标毫无关系的细节,我们称之为偶然细节(incidental detail)2。这种不相关的细节使得场景很难阅读,而这又会让利益相关人对阅读场景失去兴趣。我们去掉密码,把场景重写一下:

Scenario: Check inbox
 Given a User "Dave"
 And a User "Sue"
 And an email to "Dave" from "Sue"
 When I sign in as "Dave"
 Then I should see 1 email from "Sue" in my inbox

这绝对是一种改善,它使场景的实质性内容更易于阅读和理解了。我们进一步去掉更多噪音:

Scenario: Check inbox
 Given I have received an email from "Sue"
 When I sign in
 Then I should see 1 email from "Sue" in my inbox

现在我们有了一个简洁清晰的三步场景。可维护性也更好:如果产品负责人(product owner)想让我们修改身份验证机制,我们只需修改下层的步骤定义代码,而不必去动特性。

避免偶然细节
如果你是一名程序员,或许早已熟练了每天在阅读代码的时候滤掉不相关的细节。编写场景的时候更要时刻想着这一点,因为一不留神这些偶然细节就会溜进来。

编写场景的时候,尽力避免被已有的步骤定义所左右,只管用直白的英语把你希望发生的事情确切地写下来即可。事实上,尽量不要让程序员或测试人员独自编写场景。让非技术的利益相关人或者分析师从纯粹以业务为中心的角度出发编写每个场景的初稿,或者最理想的情况是与程序员结对,从而分享他们的构思模型。有了工程意义上设计精良的支持层,你便可以信心百倍地快速编写新的步骤定义来配合场景的表达方式。

6.2.2 命令式步骤
要让计算机为你做事,你需要给它提供指令,在计算机程序设计中,关于如何表达这些指令有两种对比鲜明的风格,分别称为命令式编程(imperative programming)和声明式编程(declarative programming)。

命令式编程是指使用一个命令序列,让计算机按特定的次序执行它们。Ruby就是命令式语言的例子:你把程序写成一系列的语句,Ruby按顺序每次执行其中的一条语句。声明式编程则告诉计算机应该做什么(what),而并不精确指明如何(how)去做。CSS就是声明式语言的例子:你告诉计算机希望Web页面上的各种元素如何呈现,剩下的让计算机去处理。

Gherkin当然是命令式语言。Cucumber按照你编写的顺序依次执行场景中的每个步骤,每次执行一步。但这并不意味着这些指令读起来要像装配组合家具的说明书一样。我们先来看看用命令式风格编写场景步骤的典型例子:

Scenario: Redirect user to originally requested page after logging in
 Given a User "dave" exists with password "secret"
 And I am not logged in
 When I navigate to the home page
 Then I am redirected to the login form
 When I fill in "Username" with "dave"
 And I fill in "Password" with "secret"
 And I press "Login"
 Then I should be on the home page

这个场景有什么好呢?好吧,它使用了非常通用的步骤的定义,如/^I fill in "(.)" with "(.)"$/"之类,这意味着你可以编写大量与之类似的场景,而无须创建太多的步骤定义代码。你或许还可以说它可以指导用户界面的设计,因为它为登录表单中使用的字段和按钮都取好了名字。

然而,团队如果使用这样一种命令式风格来编写步骤定义,用不了多久他们就会遭受脆弱的测试以及厌倦的利益相关人等痛苦。以这种方式编写的场景不仅嘈杂、冗长,读起来令人厌烦,而且很容易遭到破坏:如果负责用户体验的同事决定将提交按钮的措辞由Login改为Log in,场景就会失败,几乎莫名其妙地失败。

最严重的是,使用这样的泛化步骤定义写出的场景无法创建出场景的领域语言。基于fill in和press这样的词语,这一场景的语言所表达的领域是用户界面控件,属于泛化且层次较低的一个领域。

改用声明式风格
我们来把场景的抽象层次提高一下,通过更加声明式的风格来重写场景:

Scenario: Redirect user to originally requested page after logging in
 Given I am an unauthenticated User
 When I attempt to view some restricted content
 Then I am shown a login form
 When I authenticate with valid credentials
 Then I should be shown the restricted content

这种风格的漂亮之处在于它不与用户界面的任何特定实现相耦合。同样的场景可应用于胖客户端,也可应用于移动应用。它使用的不是技术词语,而是用一种任何对网络安全感兴趣的利益相关人都能明确理解的语言(unauthenticated、restricted、credentials)编写的。唯有从这样的抽象层次上表达每一个场景,团队的通用语言方得显现。

从命令式到声明式的风格频谱

Gherkin特性中在命令式风格和声明式风格之间并没有明确的界线。相反,这是一个连续的频谱,每个场景中每个步骤在频谱上的正确位置取决于许多方面:你所描述的系统领域,你所构建的应用类型,程序员的领域知识,以及非技术利益相关人对程序员的信任水平。如果利益相关人希望在特性中看到许多细节,这或许表明你需要努力改善这一信任,但也可能说明你们开发的系统就是需要详述很多细 节的。

声明式风格也可能被用得太过,从场景中去掉的细节太多,结果它连一个故事都讲不具体了:

Scenario: The whole system
 Given the system exists
 When I use it
 Then it should work, perfectly

这个场景当然是荒谬可笑的,但它说明了当你把抽象层次抬得太高,以至于场景不能告诉阅读者任何能引起兴趣的内容时结果会怎样。使用这一场景的团队需要对它们的程序员有不可思议的信任程度。我们鼓励你督促团队向频谱中更抽象、更声明式的一端努力,然而,最重要的永远是跟你们的利益相关人一起工作,从而找出最适合他们的抽象层次。
使用命令式风格的确意味着你必须编写更多的步骤定义,但你可以把实际工作推给支持代码中的辅助方法(helper method),从而使步骤定义的代码保持简短和易于维护。我们将在第8章向你展示这种做法。

6.2.3 重复
所有好的计算机程序员都明白重复对于代码的可维护性有多大的害处,然而我们还是经常看到团队的Cucumber特性中充满了重复。重复显然会让你的场景脆弱不堪,此外也让场景读起来单调乏味。

我们在第5章中演示过,Gherkin提供了可用来减少重复的Background和Scenario Outline关键字,但有时重复是一个信号,说明编写步骤所用的抽象层次太低,要对这种情况保持警觉。最好跟团队中的非技术成员一道工作,获得他们的反馈:哪种重复他们可以接受,哪种重复会让他们目光呆滞。

6.2.4 语言不通用
团队使用的通用语言将由系统涉及的领域来驱动。如果你们在构建面向现场音乐爱好者的系统,通用语言将包含音乐会、演出、表演者和场地之类的词语。如果你们在做电视节目预告,通用语言中将有播音员、节目类型、节目长度和播出日期之类的词语。

关键在于团队的所有成员在所有场合都使用同样的词语。如果一个数据库表名叫tbl_Performer,而其中的数据行所表示的东西被团队中多数人称为artists,那是不可接受的。每当出现这样的术语分歧的时候,大家应该马上停下来,确定哪个才是应该使用的,适当纠正后就坚持使用它。

我们讨论的是开发一种通用语言,因为这是一个持续进行的过程。这一开发过程需要我们工作上的投入。真正做到彼此倾听并且就使用的词语达成一致是需要努力的,而坚持这种约定也是需要纪律的。

然而,回报是巨大的。使用通用语言的团队犯错更少,且更能享受工作的乐趣,因为他们能有效地沟通工作内容。不重视通用语言价值的团队将会粗枝大叶地使用场景中的语汇,从而丧失在团队中专注技术和专注业务的双方之间构筑坚固桥梁的宝贵机会。那时,如果你试图纠正别人或澄清术语,带给人的感觉只会是吹毛求疵。

花点时间向团队解释一下通用语言的概念及益处。一旦大家都理解它为什么重要,你会发现大家会更乐于花精力讨论并决定使用哪些词语更合适。

如果用得正确,Cucumber可以帮助团队把通用语言开发出来。在程序员和业务人员协同编写场景的时候,你会发现关于用词精确性的各种争论会时不时地暴发。非常好!每一次分歧都暴露了两班人马之间一处潜在的误解,或者说一个bug。对新团队来说,这样的讨论开始会困难一些,但随着这一语言的不断开发,事情将变得越来越容易。下面的“三位朋友”提供了组织这种会议一种好方法。

6.2.5 闭门造车式的特性
人们会觉得Cucumber是一种技术性较强的工具。它从命令行运行,特性文件也被设计成需要与被测代码一道签入版本控制系统。然而它却以帮助提高团队的业务利益相关人对开发过程的控制感为目标。当测试和开发尽情把特性塞入版本控制的时候,团队中其他人会觉得他们的文档被锁进了柜子,而他们却没有钥匙。

你的Gherkin特性可以充当描述新特性的设计工具,同时对系统已有的行为也是极好的参考文档。对一个具备显著规模的系统来说,没有哪个人都准确记住它在每种情况下的行为,因此,当你收到来自用户的bug报告,或者考虑为系统的某部分加入新功能的时候,自然希望这些参考就在触手可及的地方。
对于分享特性从而让非技术成员也能访问,Cucumber本身只提供了有限的支持,但在Cucumber周围,大量能够提供这种支持的插件和工具不断出现。举例来说,如果用GitHub做版本控制,你的项目页面会显示语法高亮的特性,人们甚至可以在上面添加评论。

Relish4是由来自Cucumber和RSpec团队的成员创建的一项服务,旨在提供一种方便的途径来将Cucumber特性作为文档发布。RSpec项目目前就在用自己的Relish文档作为主页,你的团队也可以这么使用。

你只需要坚持做到同业务利益相关人一起坐下来协作编写场景,就足以收获Cucumber至少一半的好处。这一过程所激发的交谈会解开太多太多的潜在bug或时间延误,即便你从不打算将特性自动化,也已经收获颇丰了。

然而,如果你还是希望自动化,那就继续阅读接下来的内容,看看到底怎样做才好。

时间: 2024-11-10 05:33:20

《Cucumber:行为驱动开发指南》——6.2 同心协力的相关文章

“Cucumber行为驱动开发指南”能带给我们什么

介绍 或许你已经了解到了软件开发中一个头疼的事,就是如何产生正确的需求和围绕这些需求如何有效地进行软件开发?但又不知如何着手? 或许你已经了解到了一些相关的理论知识来解决这个难题,如:行为驱动开发(BDD),验收测试驱动开发(ATDD),实例化需求(Specification By Example),但却发现很难消化所有的信息? 或许你已经建立了一套相关的自动化测试,但总觉得在为测试而测试,没有解决实际问题,有点脱钩? 或许你已经开始着手建立自动化测试来做保障,但对那么多的工具无从选择? 也或许

《Cucumber:行为驱动开发指南》——第1章 为何使用Cucumber 1.1自动化验收测试

第1章 为何使用Cucumber 软件始于一个想法. 我们假设这是一个优秀的想法--一个能让世界变得更加美好,或者至少能让一些人赚到一些钱的想法.而软件开发人员所面临的挑战就是要落实这个想法,使其能真正产生效益. 最初的想法是完美.漂亮的.如果拥有该想法的人碰巧是一个天才软件开发人员,那事情就非常简单了:他无须向任何人解释就能直接把想法实现成可工作的软件.然而更常见的情况是,拥有最初想法的人并不具备使其想法变为现实所必需的编程技能,因此这个想法必须从他的脑中传递到另外一些人的脑中.也就是说,相关

《Cucumber:行为驱动开发指南》——1.2 行为驱动开发

1.2 行为驱动开发 行为驱动开发(Behaviour-Driven Development,BDD)1建立于测试驱动开发的基础之上,它标准化了那些优秀TDD实践者的良好习惯.优秀的TDD实践者以自外向内的方式开发软件,最初他们会编写一个失败的客户验收测试,该测试从客户的视角描述系统的行为.作为BDD实践者,我们细心编写验收测试,作为所有团队成员都能读懂的实例.我们使用这个编写实例的过程来获取业务人员的反馈,以便在开始实现软件之前,我们就知道自己是否是在编写正确的软件.在此过程中,我们会主动开发

《Cucumber:行为驱动开发指南》——2.8 让测试通过

2.8 让测试通过 既然已经有了可靠的失败场景,那就是时候让这个Cucumber场景指导我们编写解决方案了. 有一个非常简单的方案能让测试通过,但该方案其实不会有实际的帮助,不管怎样我们先试一下,哪怕为了好玩儿: 下载first_taste/08/calc.rb print "4" 试试运行cucumber,你会看到场景最终通过了: ... 1 scenario (1 passed) 3 steps (3 passed) 0m0.025s 很好!不过这个方案有什么问题呢?毕竟我们说过希

《Cucumber:行为驱动开发指南》——6.3 照管好你的测试

6.3 照管好你的测试 自动化特性的好处在于你可以把它们作为活文档来长期信赖,因为你会将每一个场景都用于检查产品代码,以确保它们仍然有效.对于同代码打交道的程序员来说,这还有另一件好处:在他们开发系统的时候,那些测试可以充当安全网,对任何破坏已有行为的错误都给出警告. 因此,你的特性可以充当一种反馈机制,对整个团队来说提供关于系统行为的反馈,对程序员来说还能提供是否破坏已有行为的反馈.想让这些反馈循环带来好处,测试需要执行迅速,还需要可靠.我们首先来看看影响测试可靠性的问题.6.3.1 渗露的场

《Cucumber:行为驱动开发指南》——第6章 Cucumber常见问题及解决之道 6.1感受痛苦

第6章 Cucumber常见问题及解决之道 如果团队是第一次用Cucumber,用不了多久你就会注意到自己写的代码bug比以前少了.你发现自己可以勇敢地重构那些以前碰都不敢碰的代码.看到自己的第一个场景通过时的那种喜悦,鼓舞着你不断添加一项又一项特性. 然而,一段时间后,事情开始变味了.突然间你发现测试运行的时间实在太长:或者你开始注意到有几个场景会随机地失败,而且通常是在紧张的工期已经临近的时候:也可能不懂技术的利益相关人对这种开发过程兴趣渐失,只剩下开发人员还在阅读那些特性.人们甚至开始问这

《Cucumber:行为驱动开发指南》——2.1 理解我们的目标

第2章 Cucumber初体验 我猜你已经等不及要立刻试试自己的"新玩具"了,下面我们通过一个简单的例子来体会一下使用Cucumber工作是怎样的感觉.在此过程中你也许无法完全理解其中的某些部分,但不必担心,接下来的几章我们会回过头来补充相应的细节. 我们将以由外向内的方式构建一个简单的命令行应用程序,整个开发过程由Cucumber驱动.注意我们是如何小步前进的,每次修改之后我们都会回头运行一下 Cucumber.这种不厌其烦的节奏对于有效使用Cucumber非常重要,关于这一点,实际

《Cucumber:行为驱动开发指南》——1.5 我们学到了什么

1.5 我们学到了什么 我们来回顾一下到目前为止讨论了哪些内容. 只有开发人员和利益相关人一起清晰地交流的时候,软件团队才能工作得最好.要做到这一点有一种非常好的方法,就是让开发人员和业务人员基于自动化验收测试,协作描述需要完成的工作. 当验收测试以实例的形式编写时,它就能够激发人们的想象力,帮助人们发现之前未曾虑及的其他场景. 当团队协作编写验收测试时,他们可以开发出专属于相应问题领域的通用语言.这能帮助他们避免误解. Cucumber 的设计就是要帮助利益相关人参与到编写验收测试的过程中去.

《Cucumber:行为驱动开发指南》——2.9 我们学到了什么

2.9 我们学到了什么 在本章中我们快速浏览了许多不同的内容,所有这些内容都会在后面再次详细介绍,现在来做一个简单的小结并强调几个最重要的地方. 2.9.1 目录结构Cucumber希望你用约定的目录结构来存储特性和步骤定义: features/ adding.feature - step_definitions/ calculator_steps.rb - 如果你真的需要,也可以传递参数给Cucumber,从而改变默认结构,优先使用自己指定的目录结构,但这种按约定存储文件的方法是最简单的. 2