这是David Chelimsky写的一篇RSpec简明指南,原文在这里。
简介
要了解RSpec,我们首先需要了解什么是行为驱动开发(Behaviour Driven Development,简称BDD),BDD是一种融合了可接受性测试驱动计划(Acceptance Test Driven Planning),域驱动设计(Domain Driven Design)以及测试驱动开发(Test Driven Development,简称TDD)的敏捷开发模型。RSpec为BDD开发提供TDD支持。
你可以简单的将RSpec看作一个传统的单元测试框架,但我们更愿意将它看成是一种领域特定语言(Domain Specific Language,以下简称DSL),它的主要作用就是描述我们对系统执行某个样例(example)的期望行为(behavior)。
这篇指南遵从TDD思想,但是我们将使用行为(behavior)和样例(example)来代替测试例(test case)和测试方法(test method),想知道我们为什么采用这样的术语,请参看Dan North, Dave Astels, 以及 Brian Marick 的相关文章。
安装
目前RSpec的最新版本是1.0.5,需要Ruby184以上版本,可以通过下面这条命令安装:
# gem install rspec
准备工作
整篇指南都围绕一个例子展开,因此在开始前,你最好先为这个例子建个目录:
$ mkdir rspec_tutorial
$ cd rspec_tutorial
开始
我们首先要了解的是RSpec DSL的”describe”与”it”方法,这两个方法有很多其它的名字(但是我们不推荐使用它们),我们之所以使用这样的命名,只是想让你站在行为(behavior)而不是结构(structure)的角度进行思考。
创建名为user_spec.rb的文件:
describe User do
end
describe方法创建一个Behavior实例,所以你可以将”describe User”理解为”描述用户的行为(describe the behaviour of the User class)”,或许这个方法叫做“
describe_the_behaviour_of”会更合适些,但这实在太冗长了,所以我们决定只选取第一个单词describe来作为这个方法的名字。
现在你可以在shell中试试这条命令:
$ spec user_spec.rb
spec命令有很多选项,但大部分超出了本指南的范围,如果你感兴趣,可以只输入spec而不带任何参数来查看帮助信息。
让我们接着回到上面那条命令,它应该会产生下面的输出:
./user_spec.rb:1: uninitialized constant User (NameError)
这是因为我们还没有创建User类,也就是说我们要描述的东西不存在,因此我们需要再创建一个user.rb来定义我们所要描述的对象:
class User
end
并在user_spec.rb中包含它:
require 'user'
describe User do
end
现在再次运行spec命令:
$ spec user_spec.rb
Finished in 6.0e-06 seconds
0 examples, 0 failures
这个输出是说我们还没有定义样例,所以现在我们就来定义一个:
describe User do
it "should be in any roles assigned to it" do
end
end
it方法返回一个Example实例,因此我们可以将it方法理解成“用户行为的一个样例”。
再次运行spec:
$ spec user_spec.rb --format specdoc
User
- should be in any roles assigned to it
Finished in 0.022865 seconds
1 example, 0 failures
specdoc参数格式化行为(describe方法创建的对象)以及样例(it方法创建的对象)的名字然后输出,这种格式来自于TestDox,一个为JUnit测试例及方法提供相似报告的工具。
现在我们开始增加Ruby代码:
describe User do
it "should be in any roles assigned to it" do
user.should be_in_role("assigned role")
end
end
这句话的意思是User应该能够胜任所有分配给他的角色,那么事实是这样么?让我们运行spec试试看:
$ spec user_spec.rb --format specdoc
User
- should be in any roles assigned to it (ERROR - 1)
1)
NameError in ‘User should be in any roles assigned to it’
undefined local variable or method `user’ for #<#:0×14ecdd8>
./user_spec.rb:6:
Finished in 0.017956 seconds
1 example, 1 failure0x14ed15c>
又出错了,是的,但在继续之前,让我们先仔细看看这段出错信息:
- “ERROR -1)”告诉我们”should be in any roles assigned to it”这个样例出错了
- “1)”则为我们详细描述了这个错误,当样例很多时,你就会发现这个编号非常有用
还有一点需要注意:这段信息没有给出RSpec代码的backtrace,如果你需要它,可以通过–backtrace选项来获取。
下面,我们继续我们的例子,上面的错误是因为我们没有创建User对象,那我们就创建一个:
describe User do
it "should be in any roles assigned to it" do
user = User.new
user.should be_in_role("assigned role")
end
end
$ spec user_spec.rb --format specdoc
User
- should be in any roles assigned to it (ERROR - 1)
1)
NoMethodError in ‘User should be in any roles assigned to it’
undefined method `in_role?’ for #
./user_spec.rb:7:
Finished in 0.020779 seconds
1 example, 1 failure0x14ec8ec>
还是失败,不过这次是因为User对象缺少role_in?方法,修改user.rb:
class User
def in_role?(role)
end
$ spec user_spec.rb --format specdoc
User
- should be in any roles assigned to it (FAILED - 1)
1)
‘User should be in any roles assigned to it’ FAILED
expected in_role?(”assigned role”) to return true, got nil
./user_spec.rb:7:
Finished in 0.0172110000000001 seconds
1 example, 1 failure
虽然又失败了,但我们的第一个目标其实已经达到了,我们得到了一段更有意义的错误描述”User should be in any roles assigned to it”。
让这段代码避免失败很简单:
class User
def in_role?(role)
true
end
$ spec user_spec.rb --format specdoc
User
- should be in any roles assigned to it
Finished in 0.018173 seconds
1 example, 0 failures
现在终于通过了,但是让我们再来看看这段代码:
describe User do
it "should be in any roles assigned to it" do
user = User.new
user.should be_in_role("assigned role")
end
end
我们可以将这个样例理解成“用户应该接受所有分配给他的角色”,但问题是我们还没有分给他角色呢?
describe User do
it "should be in any roles assigned to it" do
user = User.new
user.assign_role("assigned role")
user.should be_in_role("assigned role")
end
end
这段代码又会引发一个错误,因为User并没有assign_role这个方法:
class User
def in_role?(role)
true
end
def assign_role(role)
end
end
$ spec user_spec.rb --format specdoc
User
- should be in any roles assigned to it
Finished in 0.018998 seconds
1 example, 0 failures
样例再次通过,但是我们的任务还没结束,只要你再回头看看我们目前的代码,就会发现这个User的行为与我们的目标还有距离。
现在,我们只是解决了“用户必须接受所有分配给他的角色”,但是还有一个问题就是”用户不应该接受没有分配给他的角色“。所以我们需要为用户行为再增加一个样例:
describe User do
it "should be in any roles assigned to it" do
user = User.new
user.assign_role("assigned role")
user.should be_in_role("assigned role")
end
it “should NOT be in any roles not assigned to it” do
user = User.new
user.should_not be_in_role(”unassigned role”)
end
end
$ spec user_spec.rb --format specdoc
User
- should be in any roles assigned to it
- should NOT be in any roles not assigned to it (FAILED - 1)
1)
‘User should NOT be in any roles not assigned to it’ FAILED
expected in_role?(”unassigned role”) to return false, got true
./user_spec.rb:12:
Finished in 0.019014 seconds
2 examples, 1 failure
失败了,用户接受了没有分给他的角色,这需要我们对User的实现做些改动:
class User
def in_role?(role)
role == "assigned role"
end
def assign_role(role)
end
end
现在,一切都搞定了,但是我们的代码与样例有些重复(它们都使用了”assigned role”),因此,有必要对User类进行重构:
class User
def in_role?(role)
role == @role
end
def assign_role(role)
@role = role
end
end
随后,让我们再来测试一下:
$ spec user_spec.rb --format specdoc
User
- should be in any roles assigned to it
- should NOT be in any roles not assigned to it
Finished in 0.018199 seconds
2 examples, 0 failures
事情就这么结束了么?你可能还有些疑惑,因为我们甚至可以将一个数字分配给用户,但这与”用户应该接受任何分配给他的角色”是吻合的,所以,这时候 我们应该征求下我们的客户的意见,“每个用户在同一时间只能担当一个角色吗?”,如果客户的回答是Yes,那么很幸运,我们不需要对我们的代码进行改动, 而只需对样例的描述进行一些修改,但如果客户的回答是No,那我们恐怕还得再做些工作。
相关推荐
《RSpec Book》是一本专注于Rspec的权威指南,它详细阐述了如何使用Rspec这个强大的测试框架进行行为驱动开发(BDD)。Rspec是Ruby编程语言中的一个测试库,它使得编写可读性强、表达力丰富的测试代码成为可能。这...
### RSpec 入门者学习知识点详解 #### 一、RSpec 概述 RSpec 是一个流行的 Ruby 测试框架,主要用于行为驱动开发 (Behavior-Driven Development, BDD)。RSpec 的设计目的是让测试更加自然和易读,使得开发人员能够...
《RSpec测试:行为驱动开发与RSpec、Cucumber及其他工具》 RSpec是一种用于Ruby语言的单元测试框架,它提倡一种称为“行为驱动开发”(Behavior Driven Development,BDD)的测试方式。RSpec允许开发者以自然语言的...
综上所述,RSpec 3.1中文版是一本针对Rails开发者,特别是希望学习或提高其RSpec使用技能的开发者的实践指南。它不仅涵盖了RSpec的安装与基础使用,还包括了在Rails应用中实现TDD的具体方法和技巧,以及如何有效地将...
该书可以被看作是RSpec的权威指南,同时还能教会读者如何保持测试的清洁和可维护性。 此外,《RSpec书》还提到了“第二代XP工具”。这些工具由它们的创造者和维护者解释,对于那些想要更深入理解XP(极限编程)实践...
#### 一、RSpec框架简介与特性 **RSpec** 是 Ruby 社区中最受欢迎的行为驱动开发(Behavior Driven Development, BDD)框架之一。它为开发者提供了一种灵活的方式来定义应用程序的行为,并通过简洁易读的语法来编写...
rspec_api_documentation, 从RSpec自动生成API文档 RSpec Doc为你的Rails API生成漂亮的。查看一个示例文件。更改请查看维基以了解最新的更改。安装将rspec_api_documentation添加到你的文件gem 'rspec_a
### 使用RSpec 测试Rails 程序的知识点总结 #### 一、RSpec与Rails结合的基础概念 **RSpec**(RSpec is not a unit testing framework)是一种为Ruby编程语言设计的行为驱动开发(BDD)框架,而**Rails**是基于...
标题 "jruby-1.5.5+OperaWatir+RSpec" 暗示了这是一个关于使用 JRuby 1.5.5 版本、OperaWatir 和 RSpec 进行自动化测试的项目或者资源集合。现在,我们将深入探讨这三个关键组件以及它们在 IT 领域中的应用。 JRuby...
### RSpec Essentials: Key Insights and Learning Points **RSpec Essentials** is an essential guide for developers looking to enhance their skills in testing Ruby applications using the RSpec framework...
RSpec样式指南榜样很重要。 -军官Alex J. Murphy / RoboCop 小费您可以在找到本指南的精美版本,并对其导航进行了改进。 该RSpec样式指南概述了推荐给现实世界程序员的最佳实践,以编写其他现实世界程序员可以维护的...
- **官方文档**:RSpec 官方文档是学习RSpec的最佳起点,其中包含了详细的安装指南、使用手册和API参考。 - **第三方库**:除了RSpec本身之外,还有许多第三方库可以增强RSpec的功能,例如RSpec-Rails、RSpec-Mocks...
rspec-api-blueprint-formatter, 从RSpec测试自动生成API文档 ! RSpec APIBlueprint格式化程序从RSpec测试自动生成API文档 !像这样it 'retrievs the patients medications' do retrieve_medications
rspec-collection_matchers, 集合基数匹配器,从rspec期望中提取 RSpec::CollectionMatchers RSpec::CollectionMatchers 让你在一个例子中表达一个对象集合的预期结果。expect(account.shopping_cart).to have_
《RSpec Book》是关于行为驱动开发(BDD)的一本权威书籍,特别是针对Rspec这一Ruby语言的测试框架。本书的最新版包含了Cucumber章节,使得读者能够更好地理解和实践BDD理念。 行为驱动开发(BDD)是一种软件开发...
rspec_junit_formatter, RSpec结果格式化为你的CI可以读取的JUnit RSpec JUnit格式化程序 RSpec 2 & 3结果, Jenkins可以读取。 可能还有其他的CI服务。灵感来自于的工作,在的RSpec格式化程序在对 Reporter的失望...
Ruby提供了两种强大的测试工具,RSpec和Minitest,它们通过匹配器功能可以帮助我们预防这种问题的发生。 RSpec是Ruby中广泛使用的BDD(行为驱动开发)框架,它允许开发者以自然语言的方式编写测试。匹配器是RSpec的...
《RSpec Book》不仅是一本关于RSpec的教程,更是一本介绍如何进行行为驱动开发的指南。通过阅读本书,开发者可以深入了解BDD的概念和实践方法,并学会如何利用RSpec和Cucumber等工具来实施BDD流程。此外,书中还会...