发布于 2026-01-06 0 阅读
0

如何使用 BDD 构建坚如磐石的 Ruby on Rails 应用程序 DEV 的全球展示与讲述挑战赛,由 Mux 呈现:展示你的项目!

如何使用 BDD 构建坚如磐石的 Ruby on Rails 应用

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

十年前我开发了我的第一个 Rails 应用。我尝试过各种方法,如果说有什么是我确信的,那就是我离不开测试。而且,先写测试对我提升编程技能帮助最大。

很简单。我们希望在项目进行到第1000天时,依然能保持和项目第一天一样的效率。我们想要快速高效。为此,我们需要简洁的代码。

我们不可能一次就把所有事情都做好,所以需要重构。但是,我们不能总是担心重构会破坏现有功能,导致 bug 在不知不觉中被部署到生产环境。我们需要有信心,一旦代码出现问题,就能立即发现并修复。

信心从何而来?自动化测试套件赋予我们信心。只要测试通过,我们就可以放心地修改、删除或添加新代码,而不会出现任何重大问题。

如果测试是基础,那就先写测试。坚持一段时间,你会发现你的代码和测试都会变得多么简洁高效。

理解“行为”视角

在应用测试驱动开发 (TDD) 时,开发人员很容易陷入使用单元测试来测试对象或方法什么的陷阱,而不是测试它什么,而后者更有用。

例如,我们可以编写一个测试,断言评论集合确实是一个数组,而不是它的某个独特属性,例如按时间排序。在大多数情况下,即使我们更改该集合的实现方式,使其返回一个自定义的可枚举类,也不会造成什么影响。更一般地说:

只要对象的功能保持不变,改变对象的实现方式就不应该破坏其测试套件。

行为驱动发展(BDD)注重行为——即事物在各个发展阶段所做的事情。

起初,“行为”这个词可能听起来有些陌生。换个角度来说,我们可以把它想象成描述。我们可以向别人描述每一个底层方法、对象、按钮或屏幕——而我们描述的正是它们的行为。采用这种方法会改变我们编写代码的方式。

“给定/当/那么”沟通模式

软件开发中的大多数问题都是沟通问题。例如,产品经理未能详细描述拟议功能的每个使用场景;开发人员误解了功能的范围;产品团队缺乏验证功能是否完成的流程。

BDD简化了我们用来描述软件使用场景的语言:

在某种背景或世界状态下,

某事发生时,

然后我们期待一些结果。

“Given”、“When”、“Then”这几个简单的词语,我们可以用来同样出色地描述复杂的功能、代码对象或单个方法。这种模式是团队中所有成员,无论担任何种角色,都能理解的。

这些表达式也内置于许多测试框架中,例如Cucumber。清晰地阐述问题以及我们需要实现的解决方案有助于我们编写更好的代码。

Rails 的 BDD 工具概述

Ruby on Rails 是第一个自带集成测试框架的 Web 框架。这为该领域的进一步发展奠定了基础。

与此同时,Ruby 的表达能力以及使用 Rails 开发 Web 应用程序的效率提升,很早就吸引了许多经验丰富、声名显赫的工程师加入到这个社区。

当您使用默认选项生成新的 Rails 应用程序时,它会默认使用test/unitRuby 自带的测试库 RSpec 进行测试。不过,还有一些工具可以简化 BDD 的应用。我推荐使用RSpec作为主要的测试库,并使用Cucumber来编写高级验收测试。

RSpec

RSpec 是一个流行的 Ruby BDD 测试库。使用 RSpec 编写的测试(称为 spec)是代码在特定上下文中预期行为的可执行示例。阅读以下代码可以更容易地理解这一点:

describe ShoppingCart do
  context "when first created" do
    it "is empty" do
      shopping_cart = ShoppingCart.new
      expect(shopping_cart).to be_empty
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

编写良好的规范易于阅读,因此也易于理解。试着大声朗读上面的代码片段。我们正在描述一个购物车,说明在给定一个空白上下文的情况下,当我们创建一个新的购物车时,我们会expect(shopping_cart).to be_empty……

运行此规范会产生类似于规范的输出:

ShoppingCart
  when first created
    is empty
Enter fullscreen mode Exit fullscreen mode

我们可以使用 RSpec 来描述整个系统,但我们也可以使用一个工具来帮助我们更自然地编写和交流。

黄瓜

正如我在本指南第一章中所解释的,我们希望对每个新功能的分析阶段进行测试驱动开发。为此,我们需要验收测试来驱动实际实现该功能的代码开发。

如果你是一名在足够复杂的组织中工作的开发人员,你可能希望由其他人,例如客户或产品经理,来为你编写验收测试(免责声明:我从未在这样的环境中工作过)。大多数情况下,开发人员会自己编写验收测试。这是一个很好的实践,因为它有助于我们更好地理解我们需要构建什么。Cucumber 提供了实现这一目标所需的语言和格式。

Cucumber 读取以场景形式组织的应用程序功能纯文本描述。场景中的每个步骤都使用具体的代码来实现,它从用户的角度自动执行与应用程序的交互。例如:

Feature: Reading articles
Scenario: Commenting on an article
  Given I am logged in
  And I am reading an article with "2" comments
  When I reply to the last comment
  Then the article should have "3" comments
  And I should be subscribed to follow-up comments
Enter fullscreen mode Exit fullscreen mode

如果这是一个 Web 应用程序,上述场景可以自动启动应用程序的测试实例,在 Web 浏览器中打开它,执行任何用户都会执行的步骤,然后检查是否满足某些预期。

Rails 中的 BDD 循环

在实践中,BDD 意味着一种由外而内的方法。我们首先进行验收测试,然后在视图层编写代码,最后向下推进到模型层。这种方法有助于我们尽早发现实现功能所需的任何新对象或变量,并在此基础上做出正确的设计决策。

Rails 中的 BDD 周期包含以下步骤:

  1. 首先创建一个新的 Cucumber 场景。在编写场景之前,请务必分析并理解问题。此时,你需要了解用户界面如何允许用户完成任务。不必担心场景步骤的具体实现。

  2. 运行该场景并观察其失败情况。这将告诉你哪些步骤失败了,或者哪些步骤尚未执行。最初,大多数步骤都将处于待执行状态(未定义)。

  3. 编写第一个失败或待定规范的定义。运行该场景并观察其失败情况。

  4. 使用 RSpec 的红-绿-重构循环来测试 Rails 视图的实现。你会发现视图完成其功能所需的实例变量、控制器和控制器操作。这也是实践中唯一被证明是可选的阶段。另一种方法是在进行下一步之前,先准备好视图和控制器。

  5. 使用 RSpec 的红-绿-重构循环对控制器进行测试。确保实例变量已正确赋值,并且操作响应正确。控制器通常采用模拟方法进行驱动。控制器测试完成后,您就能了解模型或领域对象应该执行哪些操作。

  6. 使用 RSpec 的红-绿-重构循环对领域对象进行测试。确保它们提供控制器和视图所需的方法。如果您正在开发一个尚无模型的新功能,现在应该生成模型和相应的数据库迁移。此时,您应该清楚地知道它们需要做什么。

  7. 一旦你实现了所有需要的对象和方法,并且相应的规范都通过了,就运行你开始的 Cucumber 场景,以确保该步骤得到满足

Ruby on Rails 中的 BDD 循环

Ruby on Rails Web 开发中的 BDD 周期

第一个场景步骤完成后,继续进行下一个场景,并重复相同的步骤。当整个场景都已实现(场景及其所有底层规范均已通过)后,请花点时间思考一下是否还有可以进一步重构的地方。

完成当前场景后,您可以继续进行下一个场景,或者将您的工作成果展示给其他人。如果您与团队合作,请创建一个拉取请求或类似的请求来进行代码审查。创建拉取请求应该会自动触发持续集成构建。当没有其他相关场景时,请将您的工作成果展示给项目经理或客户,并请他们将功能分支部署到预发布服务器,以验证您构建的内容是否正确。

本文改编自Semaphore 出版的免费电子书Rails 测试手册》。如果你读到这里,并且想看看编写行为驱动代码的实战示例,请下载这本书,并告诉我你的想法。谢谢!

最初发表于freeCodeCamp.org

文章来源:https://dev.to/markoa/how-to-build-rock-solid-ruby-on-rails-apps-with-bdd-4jg9