关闭

Rspec简介

800人阅读 评论(0) 收藏 举报
分类:

英文原文:An introduction to Rspec

      在ruby世界中有许多好用的测试框架,rspec算是其中比较流行的一个。rspec使用了和直接测试方法的不同测试思路——测试应用的行为。下面我就来解释一下怎样实用rspec来测试应用。

Rspec入门

    RSpec使用describe块及其近亲context块来封装测试代码。通常在单元测试时我们使用describe来描述类的行为:
describe Hash do
 
end

    it块被用来编写具体测试代码。下面这个例子就是为Hash类编写的一个spec测试:
describe Hash do
  it "should return a blank instance" do
    Hash.new.should == {}
  end
end

   上面就是Rspec的基础示例了。在用命令gem install rsepc安装好rspec,然后将上面的代码放入hash_spec.rb文件,键入下面的命令执行测试:
$ rspec hash_spec.rb
    测试的输出结果如下:
.
 
Finished in 0.11021 seconds
1 example, 0 failures

    你也可以实用before和after来设置测试前后需要执行的代码,在before块和after块中的设置在describe块中也有效:
describe Hash do
  before do
    @hash = Hash.new({:hello => 'world'})
  end
 
  it "should return a blank instance" do
    Hash.new.should == {}
  end
 
  it "hash the correct information in a key" do
    @hash[:hello].should == 'world'
  end
end

    上面的代码会在每个测试块执行前创建一个 @hash变量。before块可以携带两个参数——all(一次性为所有的测试块设置变量)或each(每个测试块单独设置变量)。after块和before块一样也有这两个参数,不同的仅仅是after是在测试块代码之后执行。在前一个测试执行结束需要销毁起状态时after块很有用。

RSpec术语

    通常,我们使用describe来描述方法。在方法名前使用点号“.”表示测试的是类方法,实用“#”表示测试的是实例方法,如下所示:
describe MyClass do
  describe ".class_method_1" do
  end
 
  describe "#instance_method_1" do
  end
end

    context方法主要用来将一群具有相同上下文的测试集合在一起。当使用复杂的setup和teardown代码来设置对象时特别有用。下面以真实生活中的汉堡类为例。

     假设我们要测试汉堡类Burger的#apply_ketchup方法。然而有的人却不想加番茄酱,于是我们为这两种人分别编写测试:
describe Burger do
  describe "#apply_ketchup" do
    context "with ketchup" do
      before do
        @burger = Burger.new(:ketchup => true)
        @burger.apply_ketchup
      end
 
      it "sets the ketchup flag to true" do
        @burger.has_ketchup_on_it?.should be_true
      end
    end
 
    context "without ketchup" do
      before do
        @burger = Burger.new(:ketchup => false)
        @burger.apply_ketchup
      end
 
      it "sets the ketchup flag to false" do
        @burger.has_ketchup_on_it?.should be_false
      end
    end
  end
end

整理一下

    上面的代码可以正常工作,但是却有点重复了。RSpec为我们提供了一些helper来简化代码。我们可以使用let关键字来重写上述代码,使得变量在第一次被访问时能够自动创建:
describe Burger do
  describe "#apply_ketchup" do
    context "with ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
      before  { burger.apply_ketchup }
 
      it "sets the ketchup flag to true" do
        burger.has_ketchup_on_it?.should be_true
      end
    end
 
    context "without ketchup" do
      let(:burger) { Burger.new(:ketchup => false) }
      before  { burger.apply_ketchup }
 
      it "sets the ketchup flag to false" do
        burger.has_ketchup_on_it?.should be_false
      end
    end
  end
end

     代码变得好看了些,但我们可以使用subject方法来进一步简化。subject指定的对象是rspec目前正在进行的测试针对的对象。下面我将结合specify方法使用它。specify方法和it十分相似,唯一区别是specify将测试内容作为测试描述:
describe Burger do
  describe "#apply_ketchup" do
    subject { burger }
    before  { burger.apply_ketchup }
 
    context "with ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
 
      specify { subject.has_ketchup_on_it?.should be_true }
    end
 
    context "without ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
 
      specify { subject.has_ketchup_on_it?.should be_false }
    end
  end
end

    按照rspec的约定,它会寻找以has开头并且以问号结尾的方法,下面是最终的burger_spec.rb:
class Burger
  attr_reader :options
 
  def initialize(options={})
    @options = options
  end
 
  def apply_ketchup
    @ketchup = @options[:ketchup]
  end
 
  def has_ketchup_on_it?
    @ketchup
  end
end
 
 
describe Burger do
  describe "#apply_ketchup" do
    subject { burger }
    before  { burger.apply_ketchup }
 
    context "with ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
 
      it { should have_ketchup_on_it }
    end
 
    context "without ketchup" do
      let(:burger) { Burger.new(:ketchup => false) }
 
      it { should_not have_ketchup_on_it }
    end
  end
end

汉堡需要的不仅仅是番茄酱

      在这个简短的教程中我们创建了burger类并用rspec进行了测试,并且我们根据rspec的习惯使测试代码更符合rspec的风格。我们的目的是为了更好的可读性和更好的运行代码。以后我会书写更多的符合rspec的测试,但现在,该吃午饭了。

------------------------------------------原文结束-------------------------------------------------------------

附:RSpec断言规则

    RSpec有一些常见的断言规则。Ruby的断言方法是以问号结尾并且返回true或false的方法,常见的如: empty?   nil?    instance_of? 等。在spec中的断言很简单,就是should be_去掉问号的断言方法。如:
[].should be_empty => [].empty? #passes
  [].should_not be_empty => [].empty? #fails

    除了用"be_"来前缀断言方法,也可以用"be_a_"和"be_an_"前缀,使得代码读起来更加自然:
"a string".should be_an_instance_of(String) =>"a string".instance_of?(String) #passes

  3.should be_a_kind_of(Fixnum) => 3.kind_of?(Numeric) #passes
  3.should be_a_kind_of(Numeric) => 3.kind_of?(Numeric) #passes
  3.should be_an_instance_of(Fixnum) => 3.instance_of?(Fixnum) #passes
  3.should_not be_instance_of(Numeric) => 3.instance_of?(Numeric) #fails

     Rspec也会为诸如“has_key?”之类的断言创建匹配器,要使用该特性,在断言对象上使用 should have_key(:key) 就可以了,rspec会自动在对象上调用has_key?(:key)。如:
{:a => "A"}.should have_key(:a) => {:a => "A"}.has_key?(:a) #passes
  {:a => "A"}.should have_key(:b) => {:a => "A"}.has_key?(:b) #fails

     还有一些常见的断言方法如: be  be_close  change  eql  have  be_true  be_false be_nil include raise_error respond_to throw_symbol   等等


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:193513次
    • 积分:3079
    • 等级:
    • 排名:第11256名
    • 原创:106篇
    • 转载:40篇
    • 译文:5篇
    • 评论:34条
    文章分类
    最新评论