你的老板不会欣赏 TDD:试试这个行为驱动的开发示例
已发表: 2022-03-11测试。 它经常被留到最后一分钟,然后因为你没有时间、超出预算或其他任何原因而被削减。
管理层想知道为什么开发人员不能“一次就做好”,当不同的利益相关者描述系统的不同部分时,开发人员(尤其是在大型系统上)可能会措手不及,例如盲人描述系统的故事。大象。
然而,不可避免的是,每个项目的第一步都是讨论要构建的软件或功能的行为。 客户或业务人员找到开发团队中的某个人并解释他们想要什么。
有时这些交互以敏捷用户故事的形式出现。 有时它们以设计文档的形式出现,例如 Chris Fox 去年的博客条目。 它们也可以作为 Keynote 中的流程图或模型,甚至是匆忙的电话。
仅从这些通信中,开发人员负责构建一个“正常工作”的系统。 这对于在更大系统之外工作的自由职业者来说尤其困难。
为什么测试会被削减?
这里有一个明显的差距:如果业务所有者在一开始就设想了系统的行为,那么为什么测试这些行为是否真正有效通常是被削减的步骤?
答案可能非常简单:测试通常不被视为共享资本; 他们不被认为对项目有价值,因为“他们只是为工程师服务的”,或者类似地,为单个部门或一组人提供价值。
我们如何对这些共享资本、系统行为列表进行测试? 不仅包含测试驱动开发 (TDD),还包含行为驱动开发 (BDD)。
什么是 BDD?
行为驱动开发应该关注你的代码正在实现的业务行为:代码背后的“为什么” 。 它支持以团队为中心(尤其是跨职能)的工作流程。
当开发人员和敏捷产品所有者或业务分析师坐在一起并在纯文本编辑器中编写待定规范(稍后由开发人员填写)时,我已经看到敏捷 BDD 工作得非常好:
- 业务人员指定他们希望在系统中看到的行为。
- 开发人员根据他们对系统的理解提出问题,同时从开发的角度写下所需的其他行为。
理想情况下,双方都可以参考当前系统行为列表,看看这个新功能是否会破坏现有功能。
我发现这个简单的行为给了我足够的约束,我可以像开发人员一样思考:“鉴于我必须实现这些测试,这如何将我/每个人限制在我可以在代码中实现的规范中”? 由于它们是待定规范,因此它们可以快速轻松地在大量协作中编写。
这种协作方法让我专注于该功能为最终用户提供的功能,而让业务人员在场限制我谈论行为,而不是实施。 这突出了 BDD 与 TDD 的差异。
让我们看一个行为驱动开发的例子
场景:您是负责公司会计系统的团队中的开发人员,该系统在 Rails 中实现。 有一天,一位业务人员要求您实施一个提醒系统,以提醒客户他们的待处理发票。 因为您正在按照本教程练习 BDD; (相对于 TDD),您与该业务人员坐下来开始定义行为。
您打开文本编辑器并开始为业务用户想要的行为创建待定规范:
it "adds a reminder date when an invoice is created" it "sends an email to the invoice's account's primary contact after the reminder date has passed" it "marks that the user has read the email in the invoice"
这种对开发过程中行为的关注使得测试可以用来验证您正在构建正确的功能,而不仅仅是您的代码是否正确。 请注意,措辞是商业语言,而不是系统的内部实现语言。 您不会看到或关心发票是否belongs_to
某个帐户,因为开发团队之外没有人关心这一点。
一些开发人员更喜欢现场编写测试用例,调用系统中的方法,设置期望,如下所示:
it "adds a reminder date when an invoice is created" do current_invoice = create :invoice current_invoice.reminder_date.should == 20.days.from_now end
测试套件将失败,因为我们还没有编写代码来设置reminder_date
。
相对于失败的规格
我了解编写失败规范的开发人员,但有业务人员在我身边,这对我来说从来没有用过。 错误的业务人员要么被细节分心,要么接受这些新知识并尝试对开发人员更了解的事物进行微观管理(正确的数据库设计、代码重用)。
以我的经验,对特定行为写不止一行的概述会让商务人士感到厌烦。 他们会认为这是对他们时间的不当利用,或者会急于描述他们想到的下一个行为。
BDD 与 TDD 有何不同?
让我们以不同的方式看待这个问题,使用测试驱动的开发方法,并写出待处理的测试:
it "after_create an Invoice sets a reminder date to be creation + 20 business days" it "Account#primary_payment_contact returns the current payment contact or the client project manager" it "InvoiceChecker#mailer finds invoices that are overdue and sends the email"
这些测试很有帮助,但只对一组人有帮助:工程师。 BDD 对于与跨职能产品团队的每个成员进行沟通非常有用。

您当然可以在 BDD 思维模式下通过使用未决行为进行测试优先开发。 首先,编写测试; 然后运行它(红色); 然后让它工作(绿色); 然后让它正确(重构)。
BDD 社区中的大量工作已使测试中的断言检查读起来像英语。 这是一个典型的 RSpec 测试:
a = 42 a.should == 42
这种格式使以后更容易阅读。 但请记住,这不是我们在这里所做的——关键是尽快降低行为——并执行“每个规范一个测试行为”的原则。 理想情况下,待定规范标题应该告诉您正在测试什么。
BDD 不是用花哨的方法来验证你的结果。 这是关于在团队的所有成员之间分享预期的行为。
行为驱动的开发是关于协作和沟通
让我们回到我们的场景:研究公司会计系统。
您与业务人员一起了解项目的功能,通过其内部分析系统(对象如何在内部组合在一起),然后他们从外部分析系统。
您想到一些条件,并询问业务分析师在以下场景中会发生什么:
* What's the default reminder date going to be? How many days before the invoice due date? * Are those business days or just calendar days? * What happens if there's not a primary contact associated with the account?
你得到答案。 重要的是,业务人员要明白,您并不是要在他们的宠物想法中打洞,或者过于迂腐。
通过这种方式,行为驱动开发是一个帮助协作并开始两个部门之间对话的工具。 这也是一种阐明所需功能范围并从开发团队获得更好估计的方法。 也许您意识到从给定的时间点开始计算 10 个工作日是不可能的; 这是您需要实现的附加的、单独的功能。
开发人员会有开发人员的考虑(“你说‘day’到底是什么意思?”),而业务人员会有自己的考虑(“请不要在这里使用过期这个词,这意味着不同的东西”)。 让一组或另一组去尝试自己编写这些业务逻辑行为测试(Cucumber 的承诺)会削减每一方的有价值的输入。
当敏捷客户不再实际在房间里时,它也是一个很好的替代品:将他们的愿望写在纸上,与开发人员对您正在构建的内容的分析(和翻译)相结合。
设计文件
较早的 Toptal 博客文章 Chris Fox 谈到了设计文档,尤其是在项目开始时。 理解和提取业务行为的范围从系统可以了解的项目缩减到需要数十年程序员年才能完成并拥有数百或数千个行为规范的项目。
Chris 的文章还提到了元素在屏幕上的行为,这是我经常与设计师产生分歧的一个领域:“这个按钮在昏暗的时候是什么样子的”或者“这在 11 个屏幕上看起来如何”,因为这个页面显然是为 24 英寸屏幕设计的?” 这种与业务人员的反复来回可以发现项目的图形资产或页面布局中的空白。
在非常大的跨职能团队中,有许多团队成员有自己的顾虑:设计师、开发人员、可能来自运营的人、数据库管理员——也许是 QA 人员(是的,TDD 和 BDD 中每个人都有自己的位置!)带着自己的顾虑和问题。 BDD 比测试驱动开发更能推动这种协作。 来自“当这个表的数据太大时会发生什么?” 到,“嗯,这将是一个昂贵的查询,我们想以某种方式缓存它”到,“等等,用户应该看到所有这些机密信息吗?”,可能不仅仅是开发人员和对此新功能有疑问的业务分析师
行为驱动开发是关于共享工件
什么是共享工件?
我喜欢将软件工程中的“工件”视为描述项目或项目团队的潜在物理事物,并且可以在六个月后找到。 好的人工制品解释了为什么事情是这样的。
走廊对话不是人工制品。 白板绘图也不是。 白板绘图变成了大而长的类文档或设计文档——这些都是人工制品。 会议纪要也是人工制品。
工件是保存到存储库或共享空间的一些源代码,以及票证系统中的票证,或内部 Wiki 上的注释,甚至是持久的聊天日志。
在我看来,共享工件是最好的工件。 它们不仅展示了某些东西为什么会这样,还展示了它为什么存在于应用程序中。
你如何在 BDD 中使用它们?
行为应该是一个共享的团队工件——测试不应该只是程序员的忙碌工作! 虽然最好有一个整个团队都可以轻松查看当前规范的系统(也许部署系统也会提取行为列表并将其保存到站点或 wiki 的私有区域),但您也可以手动执行每个短跑。
游戏的名称是“帮助开发人员创建我们需要的规范,以更快地交付业务价值、跨部门协作并做出更好的估计”。
这种全公司范围内对系统功能的理解也是一种资本形式。 这是代码“如何”的“为什么”。
结论
我们如何解决交付给客户的错误软件问题? 通过确保测试不被视为“只有开发人员关心”的事情。
除了代码正确性之外,描述和理解系统的需求还有很多好处:建立部门间对话并确保满足所有利益相关者的关注(而不仅仅是拥有大头衔的利益相关者)。 使用行为驱动开发从一开始就了解这些需求并测试整个团队关心的外部业务行为——这是确保项目质量的好方法。