前言
要了解RSpec,我们首先需要了解什么是行为驱动开发(Behaviour Driven Development,简称BDD),BDD是一种融合了可接受性测试驱动计划(Acceptance Test Driven Planning),域驱动设计(Domain Driven Design)以及测试驱动开发(Test Driven Development,简称TDD)的敏捷开发模型。RSpec为BDD开发提供TDD支持。
这篇文章遵从TDD思想,但是我们将使用行为(behavior)和样例(example)来代替测试例(test case)和测试方法(test method)。
介绍
· textual descriptions of examples and groups (rspec-core)
· flexible and customizable reporting
· extensible expectation language (rspec-expectations)
built-in mocking/stubbing framework (rspec-mocks)
安装
rails new rspec_tutional –T
加上-T,创建工程时不会添加测试内容,然后修改Gemfile,添加如下内容
group :test, :development do gem ‘rspec’ gem ‘rspec-rails’(两者版本最好一致) end
然后通过下面的命令创建rspec框架
rails g respec:install
此命令的作用是为Rails程序安装RSpec框架,使RSpec取代Test::Unit而存在,这条命令会建立’.rspec’文件、’spec’目录和’spec/spec_helper.rb’文件,他们分别的作用是:
‘.rspec’文件:存放运行rake spec任务时,RSpec需要加载的配置信息。
‘spec’目录:我们的RSpec测试用例的存放空间,针对mvc,我们可以建立models、views和controllers目录分别存放模型、视图和控制器各个层面的测试用例,每一个测试文件以_spec结尾。
‘spec/spec_helper.rb’文件:运行测试用例的RSpec配置文件。每一个以_spec结尾的文件都需要引入该文件,即在文件开头添加:require ‘spec_helper’代码。
TDD
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
class User
def in_role?(role)
role == @role
end
def assign_role(role)
@role = role
end
end
Expectations--- Built-in matchers
Equivalence
actual.should eq(expected) # passes if actual == expected
actual.should == expected # passes if actual == expected
actual.should eql(expected) # passes if actual.eql?(expected)
actual.should equal(expected) # passes if actual.equal?(expected)
actual.should === expected # passes if actual === expected
Identity
actual.should be(expected) # passes if actual.equal?(expected)
Comparisons
actual.should be > expected
actual.should be >= expected
actual.should be <= expected
actual.should be < expected
actual.should be_within(delta).of(expected)
Regular expressions
actual.should =~ /expression/
actual.should match(/expression/)
Types/classes
actual.should be_an_instance_of(expected)
actual.should be_a_kind_of(expected)
Truthiness
actual.should be_true # passes if actual is truthy (not nil or false)
actual.should be_false # passes if actual is falsy (nil or false)
actual.should be_nil # passes if actual is nil
Expecting errors
expect { ... }.to raise_error
expect { ... }.to raise_error(ErrorClass)
expect { ... }.to raise_error("message")
expect { ... }.to raise_error(ErrorClass, "message")
Expecting throws
expect { ... }.to throw_symbol
expect { ... }.to throw_symbol(:symbol)
expect { ... }.to throw_symbol(:symbol, 'value')
Predicate matchers
actual.should be_xxx # passes if actual.xxx?
actual.should have_xxx(:arg) # passes if actual.has_xxx?(:arg)
Ranges (Ruby >= 1.9 only)
(1..10).should cover(3)
Collection membership
actual.should include(expected)
Examples
[1,2,3].should include(1)
[1,2,3].should include(1, 2)
{:a => 'b'}.should include(:a => 'b')
"this string".should include("is str")
Core
上边是一个比较完整的体现RSpec组织测试用例的模板,当然这里只是为了说明一个_spec文件如何组织测试用例及基本用法,所以我没有去编写每一个测试用例的体,并且例子出现的字符串内容不针对于具体的系统,没有实际意义。下边依次解释含义:
require ‘spec_helper’:目的是加载’spec/spec_helper.rb’文件中的RSpec配置,运行时需要。
describe()方法:
我们用describe()方法定义一个测试用例组,describe()方法的参数可以是我们要测试的对象(如例中的People),可以是一个字符串,用来描述该组,describe方法后的字符串所描述的内容可以对应一个用户故事。
注意describe()方法是可以嵌套的,两个describe()方法衔接起来可以更细化一个用户故事,如上边的里面的describe()方法内就表示:”People have enough money pay for house”。
it()方法:
我们用it()方法定义一个具体的测试用例(在RSpec中,称一个测试用例为一个example)。其后的字符串为该方法的参数,用来描述一个具体的场景,it方法体就是我们对系统在该场景下的行为的定义。
It()方法和describe()方法衔接起来,可以完整的描述一个系统行为,以上边的最后的一个测试用例为:”People have enough money pay for house should travel ”。
context()方法:
Context()方法和describe()方法的作用一样,不同点在于describe倾向于描述我们的测试对象,而context()方法倾向于用字符串描述用户故事。
before()和after():
这两个方法很多测试框架都支持,需要说明的是这两个方法的参数,例子中为符号’:each’,表明对于每一个测试用例,先执行 before()方法中的代码,用例完成后执行after()方法中的代码,若参数为’:all’,则表示在所有的测试用例执行之前,先执行 before()方法中的代码,所有的用例完成后执行after()方法中的代码。
RSpec还提供around()方法,暂不懂其用法,之后的报告中补上。
帮助方法:
所谓的帮助方法就是把多个测试用例中重复的操作抽出作为一个公用的方法,提高代码重用度。如例子中的work_hard()方法。
共享行为:
系统的某一个行为是很多场景下都具有的,那么我们可以把它定义为一个共享行为,我们通过share_examples_for()方法 定义共享行为,使用it_behaves_like()方法共享定义过的系统行为,如例子中的share_examples_for “any people”, it_behaves_like “any people”。
pending()方法:
我们确定系统拥有一个行为,但是还没有具体定义,这时我们可以将该行为使用pending()方法来设置该行为为待定义,其后的字符串参数将在生成的报告中显示。
pending()方法的另外一种用法就是,当一个测试用例失败时,我们可以利用pending方法设置其为一个待修复的bug,方法体内包含使用例失败的代码。例如最后一个测试用例的内容