RSpec基础

25 Aug 2013, by

rspec文档
rspec源码

简单命令

./bin/rspec spec/controllers/api/v1/
rake db:drop RAILS_ENV=test
rake db:create RAILS_ENV=test
rake db:migrate RAILS_ENV=test

命令行运行测试

let 方法(定义局部变量)

我们可以使用 RSpec 提供的 let 方法便捷的在测试中定义局部变量。let 方法的句法看起来有点怪,不过和变量赋值语句的作用是一样的。let 方法的参数是一个 Symbol,后面可以跟着一个块,块中代码的返回值会赋给名为 Symbol 代表的局部变量。也就是说:

let(:name) {expression}格式

跟before里的代码一样的,但是比before里的代码效果更好。

let(:found_user) { User.find_by(email: @user.email) }

定义了一个名为 found_user 的变量,其值等于 find_by 的返回值。在这个测试用例的任何一个 before 或 it 块中都可以使用这个变量。使用 let 方法定义变量的一个好处是,它可以记住变量的值。对上面的代码而言,因为 let 的备忘功能,found_user 的值会被记住,因此不管调用多少次 User 模型测试,find_by 方法只会运行一次。

describe和context方法

严格来说,describe 和 context 是可以互换的,倾向于这么用:describe 用来表示需要实现的功能,而 context 针对该功能不同的情况。
describe和context方法用来组织相关的行为example。 使用一个字符串作为他们的参数,以及使用一个block来定义其上下文的范围。 写model的spec或者其他的unit test时,可以传一个Ruby类作为describe的第一个参数。Doing so also creates an implicit subject for the examples.

describe Timesheet do
  ...
end

describe可以嵌套
一般使用:

describe Timesheet do
  describe “#test” do
    ...
  end
end

这样的方式(注意第一个describe的参数是一个类,第二个describe参数是以#开始)这个表示测试Timesheet类下面的test方法)

its方法

它和 it 很像,不过测试对象是参数中指定的属性而不是整个测试的对象。

its(:remember_token) { should_not be_blank }

等同于:

it { expect(@user.remember_token).not_to be_blank }

before和after方法

和setup、teardown方法类似
假如测试在用例执行后需要中断与外部服务的连接,那么我们就可以使用 after 块来善后测试用例。因为 RSpec 会自行清理数据库,所以很少使用 after。
Before and after code can be inserted in any describe or context blocks, and by default the execute for each it block that shares their scope.

before(:each) 每段it之前執行
before(:all) 整段describe前只執行一次
after(:each) 每段it之後執行
after(:all) 整段describe後只執行一次

before { visit root_path } 链接,创建实例

it方法

it方法使用一个描述和block。一个it就是一个测试,最好一个it一个期望
As mentioned, the idea is to complete the thought that was started in the describe method, so that it foms a complete sentence.

specify方法

specify是it方法的别名,但是他可以使用不同的结构来增加可读性。

describe BlogPost do
  set(:blog_post) {blog_post = BlogPost.new}
  specify {blog_post.should_not be_published}
end

生成的RSpecDoc如下: BlogPost

shared_examples_for方法与it_should_behave_like方法来消除重复

subject { page }

  shared_examples_for "all static pages" do
    it { should have_content(heading) }
    it { should have_title(full_title(page_title)) }
  end

  describe "Home page" do
    before { visit root_path }
    let(:heading)    { 'Sample App' }
    let(:page_title) { '' }

    it_should_behave_like "all static pages"
    it { should_not have_title('| Home') }
  end

expect方法

expect 用来改变一个值或者抛出一个异常。后面接change来表示要达到的值,使用raise_error(异常类)来表示会抛出一个异常。
change 方法可接受两个参数,第一个参数是对象名,第二个是 Symbol。change 方法会在 expect 块中的代码执行前后,分别计算在第一个参数上调用第二参数代表的方法返回的结果

expect {
BlogPost.create :title => “Hello”
}.to change {BlogPost.count}.by(1)

希望在expect块里做完之后,BlogPost.count的值要改为1 改变值的例子

describe Order do
  let(:order) {order = Order.create}
  describe "#ship!” do
    context “with paid” do
      it "should update status to shipping" do
        expect {
          order.ship!
          }.to change { order.status }.from(“new”).to(“ship”)
       end
    end
end

这里改变值使用的from和to,这样就会在执行expect块之前检查order.status的值是不是new,并且会在执行之后检查是不是”ship”值 抛出异常的例子

describe Order do
  let(:order) {order = Order.create}
  describe "#ship!” do
    context “with paid” do
      it "should raise NotPaidError" do
        expect {
          order.paid? = flase
          order.ship!
          }.to raise_error(NotPaidError)
       end
    end
end

这里表示执行完except块之后会抛出一个NotPaidError异常。

pending方法

可以使用pending来列出打算要写的测试 使用it函数不传block给他也是pending的意思,也可以在block离调用pending

可以在before里写pending

should和should_not方法

Rspec mixes them into the base Ruby Object class at runtime so that they are available on all objects.They expect to receive Matcher objects, generated using Rspec expectation syntax

检查型别、方法:

receiver.should be_true
receiver.should be_false
receiver.should be_nil

检查Array、Hash:

receiver.should be_a_kind_of(Array)
receiver.should be_an_instance_of(Array)
receiver.should responsed_to(:foo)

receiver.should have_key(:foo)
receiver.should include(4)
receiver.should have(3).items

任何be_开头:

receiver.should be_empty
receiver.should be_blank
receiver.should be_admin
receiver.should be_valid 是否合法

should == 是万能的 Rspec的Matcher很多,也可以自己写

Implicit Subject和Explicit Subject

使用subject可省略receiver

its

its可以省略receiver的方法调用

describe Order do
  subject { Order.new}
  its(:status) {should == “New”}
end 

controller的测试(基本三段式)

常用方法

代码实例:

describe "Home page" do

  it "should have the content 'Sample App'" do
    visit '/static_pages/home'
    expect(page).to have_content('Sample App')
  end

end

运行:$ bundle exec rspec spec/requests/static_pages_spec.rb

describe User do

  before { @user = User.new(name: "Example User", email: "user@example.com") }

  subject { @user }

  it { should respond_to(:name) }
  it { should respond_to(:email) }
end
describe "signup" do

    before { visit signup_path }

    let(:submit) { "Create my account" }

    describe "with invalid information" do
      it "should not create a user" do
        expect { click_button submit }.not_to change(User, :count)
      end
    end

    describe "with valid information" do
      before do
        fill_in "Name",         with: "Example User"
        fill_in "Email",        with: "user@example.com"
        fill_in "Password",     with: "foobar"
        fill_in "Confirmation", with: "foobar"
      end

      it "should create a user" do
        expect { click_button submit }.to change(User, :count).by(1)
      end
    end
end