rspec 入門教程

這是一個(gè)簡單的關(guān)于Rails Rspec的簡單的介紹

1 安裝Rspec

在Rails的配置文件Gemfile配置文件中,配置下面信息

 group :development, :test do
  gem 'rspec-rails', '2.13.1'
 end

我們沒有必要單獨(dú)的安裝RSpec ,因?yàn)樗莚spec-rails的依賴件
會(huì)被自動(dòng)安裝.
執(zhí)行bundle install 或者 bundle install --without production 來安裝使用的gem.
rails generate rspec:install
設(shè)置讓Rails使用RSpec 而不用Test::Unit. 該命令執(zhí)行完畢之后,會(huì)產(chǎn)生一個(gè)文件夾spec,該文件加下面有spec/spec_helper.rb 這個(gè)文件,spec_helper.rb用來設(shè)置測試的配置信息.
下面是spec的固定的規(guī)范,固定的格式.

describe XXX do
  it XXX do
       ......
  end
end

包含了一個(gè) describe 塊以及其中的一個(gè)測試用例(sample),以 it "..." do 開頭的代碼塊就是一個(gè)用例.

2 pending 測試大綱
  describe xxxController do

  describe 'GET #show' do
    it "assigns the requested xxx to @xxx"
    it "renders the :show template"
  end

  describe 'GET #new' do
    it "assigns a new xxx to @xxx"
    it "renders the :new template"
  end

  describe 'GET #edit' do
    it "assigns the requested xxx to @xxx"
    it "renders the :edit template"
  end

  describe "POST #create" do
    context "with valid attributes" do
      it "saves the new xxx in the database"
      it "redirects to xxx#show"
    end

    context "with invalid attributes" do
      it "does not save the new xxx in the database"
      it "re-renders the :new template"
    end
  end

  describe 'PUT #update' do
    context "with valid attributes" do
      it "updates the xxx in the database"
      it "redirects to the xxx"
    end

    context "with invalid attributes" do
      it "does not update the xxx"
      it "re-renders the #edit template"
    end
  end

  describe 'DELETE #destroy' do
    it "deletes the xxx from the database"
    it "redirects to users#index"
  end

  describe 'GET #index' do
      it "response.status eq 200"
      it "renders the :index view"
  end
end
3 簡單的測試

測試代碼的結(jié)構(gòu)

describe AA do
  it 'should do something' do
   something.should
  end
end

使用 http://railstutorial-china.org/ 里面的列子,來做一個(gè)簡單的測試演示.
rails generate controller StaticPages home help --no-test-framework
該命令會(huì)在app/controller文件下生成static_pages_controller.rb,并且會(huì)在路由里面生成help,home對應(yīng)的路由.

match '/help',    to: 'static_pages#help',    via: 'get'
match '/home',    to: 'static_pages#home',    via: 'get'

之后運(yùn)行下面命令,產(chǎn)生spec/requests/static_pages_spec.rb
rails generate integration_test static_pages
或者直接手動(dòng)創(chuàng)建spec/requests/static_pages_spec.rb這個(gè)文件.
下面做一個(gè)簡單的測試

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

運(yùn)行bundle exec rspec spec/requests/static_pages_spec.rb, 運(yùn)行失敗!
因?yàn)樵?em>app/views/static_pages/home.html.erb文件中并沒有"Sample App"關(guān)鍵字.在該文件中,添加上"Sample App"測試就能通過.

4 let和let!的用法

Use let to define a memoized helper method. The value will be cached
across multiple calls in the same example but not across examples.
Note that let is lazy-evaluated: it is not evaluated until the first time
the method it defines is invoked. You can use let! to force the method's
invocation before each example.

http://stackoverflow.com/questions/10173097/rails-rspec-difference-between-let-and-let

使用let來定義一個(gè)memoized helper方法。該值可以在多個(gè)例子中使用,但不能跨多個(gè)實(shí)例調(diào)用。

  • spec/helpers/let_spec.rb *

    describe "let" do
    other_count = 0
    invocation_order = []

    let(:count) do
    invocation_order << :let!
    other_count += 1
    end

    it "calls the helper method in a before hook" do
    # count
    # invocation_order << :example
    # expect(invocation_order).to eq([:let!,:example])

    invocation_order << :example
    expect(invocation_order).to eq([:example])
    expect(other_count).to      eq(0)
    other_count +=1
    expect(other_count).to      eq(1)
    

    end

it "calls the helper method in a before hook again" do
  # count
  # invocation_order << :example
  # expect(invocation_order).to eq([:let!,:example])  

  invocation_order << :example
  expect(invocation_order).to eq([:example,:example])
  expect(other_count).to      eq(1)
  other_count +=1
  expect(other_count).to      eq(2)

end

end


describe "let test" do
   let(:count) { $count += 1 }
   # 可以使用count
end
describe "no let test" do
    # 不能使用count
end

下面是let!的使用方法

  • spec/helpers/let_bang_spec.rb *

    describe "let!" do
    count = 0
    invocation_order = []

    let!(:count) do
    invocation_order << :let!
    count += 1
    end

    it "calls the helper method in a before hook" do
    invocation_order << :example
    expect(invocation_order).to eq([:let!, :example])
    expect(count).to eq(1)
    end

    it "calls the helper method in a before hook again" do
    invocation_order << :example
    expect(invocation_order).to eq([:let!, :example, :let!, :example])
    expect(count).to eq(2)
    end
    end

5 before用法

http://stackoverflow.com/questions/5974360/rspec-difference-between-let-and-before-block
http://stackoverflow.com/questions/5359558/when-to-use-rspec-let?lq=1
let 和 before(:each)的區(qū)別, let不會(huì)自動(dòng)初始化變量,而before(:each)會(huì)自動(dòng)初始化變量.如果我其中的某一個(gè)測試用力不需要這些變量,依然需要初始化,如初始化變量需要很多時(shí)間,對這個(gè)測試的初始化就是浪費(fèi)的時(shí)間和資源.

before(:each)的用法

  • spec/helpers/before_each_spec.rb *

    require "rspec/expectations"
    class Thing
    def widgets
    @widgets ||= []
    end
    end
    describe Thing do
    before(:each) do
    @thing = Thing.new
    end
    describe "initialized in before(:each)" do
    it "has 0 widgets" do
    @thing.should have(0).widgets
    end
    it "can get accept new widgets" do
    @thing.widgets << Object.new
    end
    it "does not share state across examples" do
    @thing.should have(0).widgets
    end
    it "does not have 1 count" do
    @thing.should_not have(1).widgets
    end
    end
    end

before(:all)用法

  • spec/helpers/before_all_spec.rb *

    require "rspec/expectations"
    class Thing
    def widgets
    @widgets ||= []
    end
    end
    describe Thing do
    before(:all) do
    @thing = Thing.new
    end
    describe "initialized in before(:all)" do
    it "has 0 widgets" do
    @thing.should have(0).widgets
    end
    it "can get accept new widgets" do
    @thing.widgets << Object.new
    end
    it "shares state across examples" do
    @thing.should have(1).widgets
    end
    it "should not have 0 widgets" do
    @thing.should_not have(0).widgets
    end
    end
    end

6 model 測試

spec/models/widget_spec.rb

describe Widget do
  context "test fields" do
    before do
      @widget = create(:widget)
    end

    subject{ @widget }
    # respond_to 用來判斷屬性有沒有
    it { should respond_to(:name) }
    it { should respond_to(:email) }
    it { should respond_to(:address) }
    it { should respond_to(:lat) }
    it { should respond_to(:lng) }
    it { should be_valid }

    # 簡約的寫法
    describe "when name is not present" do
      before { @widget.name = " " }
      it { should_not be_valid }
    end

    # 繁瑣的寫法
    describe "when name is not present" do
      before { @widget.name = " " }
      # 將it的單行的方式,改寫成多行的方式
      it "should not be valid" do
        expect(@widget).not_to   be_valid
      end
    end


    describe "when name is invalid" do
      before { @widget.name = nil }
      it { should have(1).errors_on(:name) }
    end

    describe "when name length > 50 " do
      before do
        @widget.name = "a"*51
      end
      it { should_not be_valid }
    end

    describe "when email is invalid" do
      before { @widget.email = nil }
      it do
        # Email can't be blank, Email is invalid
        should have(2).errors_on(:email)
      end
    end

    describe "when email is invalid" do
      it "should be invalid" do
        # 使用each 循環(huán),遍歷非法的email
        addresses = %w[user@foo,com user_at_foo.org example.user@foo.
                     foo@bar_baz.com foo@bar+baz.com]
        addresses.each  do |addr|
          @widget.email = addr
          expect(@widget).not_to  be_valid
        end
      end
    end

    describe "when email is valid" do
      it "should be valid"  do
      # 使用each 循環(huán),遍歷可用的email
      addresses = %w[user@foo.COM A_US-ER@f.b.org
                    frst.lst@foo.jp a+b@baz.cn]
        addresses.each do |addr|
          @widget.email = addr
          expect(@widget).to be_valid
        end
      end
    end
   # ========== 上述驗(yàn)證郵箱的可該寫成如下
   describe "when email" do
      context "is valid" do 
        it "should be valid"  do
          addresses = %w[user@foo.COM A_US-ER@f.b.org
                   frst.lst@foo.jp a+b@baz.cn]
          addresses.each do |addr|
            @widget.email = addr
            expect(@widget).to be_valid
         end
      end
    end

      context "is invalid" do
        it "should be invalid" do
          addresses = %w[user@foo,com user_at_foo.org example.user@foo.
                  foo@bar_baz.com foo@bar+baz.com]
          addresses.each  do |addr|
            @widget.email = addr
            expect(@widget).not_to  be_valid
          end
        end
    end
  end
end



  context "when initialized" do
    # 創(chuàng)建默認(rèn)的共享變量
    subject(:widget) { Widget.new(:name => Faker::Name.name,
                                  :email => Faker::Internet.email) }


    it "should increment the Relationship count" do
      expect do
        Widget.create!(:name => "first comment", :email => Faker::Internet.email)
        Widget.create!(:name => "second comment", :email => Faker::Internet.email)
      end.to change(Widget, :count).by(2) # from(1).to(3)
      # https://www.relishapp.com/rspec/rspec-expectations/v/2-0/docs/matchers/expect-change
    end

    # 第一種寫法
    it "is a new widget" do
      expect(widget).to be_a_new(Widget)
    end

    # 第二種寫法
    it { should be_a_new(Widget) }

    it "is not a new string" do
      expect(widget).not_to be_a_new(String)
    end
  end

  context "when saved" do
    subject(:widget) { create(:widget) }

    it "is not a new widget" do
      expect(widget).not_to be_a_new(Widget)

    end
    it "is not a new string" do
      expect(widget).not_to be_a_new(String)
    end
  end
end
7 controller測試

spec/controllers/widgets_controller_spec.rb

describe WidgetsController do
  # show ============
  describe "GET #show" do
   #? 和控制器動(dòng)作交互的基本 DSL 句法:每個(gè) HTTP 請求方法都對應(yīng)于一個(gè)方法(本例中的方法
   #是 get) ,其后跟著動(dòng)作的 Symbol 形式(:show) ,然后是傳入的請求參數(shù)(id: contact) 。
   #? 控制器動(dòng)作實(shí)例化的變量可以通過 assigns(:variable_name) 方法獲取。
   #? 控制器動(dòng)作的返回結(jié)果可以通過 response 獲取。
    it "assigns the required 1 to @1" do
      widget = create(:widget)
      get :show, id: widget
      expect(assigns(:widget)).to eq widget
     # http://stackoverflow.com/questions/8616508/what-does-assigns
     # 
    end

    it "renders the :show template "  do
      widget = create(:widget)
      get :show, id: widget
      expect(response).to render_template :show
    end
  end

  # new ============
  describe "GET #new" do
    it "assigns a new Widget to @widget" do
      get :new
      expect(assigns(:widget)).to  be_a_new(Widget)
    end

    it "renders the :new template" do
      get :new
      expect(response).to render_template :new
    end
  end

  # edit ============
  describe "GET #edit" do
    it "assigns the request widget to @widget" do
      widget = create(:widget)
      get :edit, id: widget
      expect(assigns(:widget)).to eq widget
    end

    it "renders the :edit template" do
      widget = create(:widget)
      get :edit, id: widget
      expect(response).to render_template :edit
    end
  end

  # create ============
  describe "POST #create" do
    context "with invalid attributes" do
      # create動(dòng)作, 在符合 REST 架構(gòu)的程序中,這個(gè)動(dòng)作可以響應(yīng) POST 請求。
      # create 動(dòng)作和響應(yīng) GET 請求的動(dòng)作最主要的不同點(diǎn)是,不能像 GET 請求那樣只傳入:id 參數(shù),而
      # 要傳入 params[:widget] 對應(yīng)的值,這個(gè)值就是用戶可能在表單中輸入的內(nèi)容。
      it "does not save the new widget in the database" do
        expect{
          post :create,
          widget: attributes_for(:invalid_widget)
        }.to change(Widget, :count).by(0)
      end

     # expect的另一種寫法
     it "does save the new widget in the database again" do
       expect do
         post :create,
         widget: attributes_for(:widget)
       end.to  change(Widget, :count).by(1)
     end

      it "re-renders the :new template" do
        post :create,
          widget: attributes_for(:invalid_widget)
        expect(response).to render_template :new
      end
    end

    context  "with valid attributes" do
      it "does save the new widget in the database" do
        expect{
          post :create,
          widget: attributes_for(:widget)
        }.to change(Widget, :count).by(1)
      end
      it "re-renders the assigns[:widget] template" do
        post :create,
          widget: attributes_for(:widget)
        expect(response).to redirect_to widget_path(assigns[:widget])
      end
    end

    # update ============
    describe "PATH #update" do
      before :each do
        @widget = create(:widget, name: 'lisi', email: 'lisi@126.com')
      end
      context "valid attribute" do
        # 先測試請求
        it "located the request @widget " do
          patch :update, id: @widget, widget: attributes_for(:widget)
          expect(assigns(:widget)).to  eq(@widget)
        end

        # 其次測試上傳的數(shù)據(jù),是否發(fā)生了變化
        it "changes @widget`s attributes" do
          patch :update, id: @widget,
            widget: attributes_for(:widget, name: "lol", email: "lol@126.com")
          @widget.reload
          expect(@widget.name).to eq("lol")
          expect(@widget.email).to eq("lol@126.com")
        end

        # 最后測試,返回結(jié)果
        it "redirect to the updated widget" do
          patch :update, id: @widget, widget: attributes_for(:widget)
          expect(response).to redirect_to @widget
        end
      end

      context "with invalid attributes" do
        it "does not change the contact`s attribues" do
          patch :update, id: @widget,
            widget: attributes_for(:widget, name: nil, email: "nil@126.com")
          @widget.reload
          expect(@widget.name).to eq("lisi")
          expect(@widget.email).not_to eq("nil@126.com")
        end

        it "re-renders the edit template" do
          patch :update, id: @widget,
            widget: attributes_for(:invalid_widget)
          expect(response).to render_template :edit
        end
      end
    end

    # delete ============
    describe "DELETE destroy" do
      before :each do
        @widget = create(:widget)
      end

      it "delete the widget" do
        expect{
          delete :destroy, id: @widget
        }.to change(Widget, :count).by(-1)
      end

      it "redirect to widget#index" do
        delete :destroy, id: @widget
        expect(response).to redirect_to widgets_path
      end
    end
  end


  describe "GET index" do
    it "has a 200 status code" do
      get :index
      expect(response.status).to eq(200)
    end

    it "renders the index template"  do
      get :index
      expect(response).to render_template("index")
      expect(response.body).to eq ""
    end

    it "renders the widgets/index template"  do
      get :index
      expect(response).to  render_template("widgets/index")
      expect(response.body).to eq ""
    end

    it "not renders the 'new' template"  do
      get :index
      expect(response).not_to  render_template("new")
    end
  end
end
8 route測試

首先運(yùn)行
rails generate controller admin/Accounts index
用這個(gè)來創(chuàng)建一個(gè)namespace為admin的account_controller.rb

  • spec/routing/admin_routing_spec.rb*

    require "spec_helper"
    describe "routes for Widgets" do
    it "routes /admin/accounts to the admin/accounts controller" do
    expect(get("/admin/accounts")).
    to route_to("admin/accounts#index")
    end
    it "routes /admin/accounts to the admin/accounts controller again" do
    expect(get("/admin/accounts")).
    to route_to(:controller => "admin/accounts", :action => "index")
    end
    end

spec/routing/widgets_routing_spec.rb

 describe "routes for Widgets" do
      it "route to widgets" do
        expect(:get => "/widgets").to be_routable
      end
      it "does not route to widgets/foo/bar" do
        expect(:get => "/widgets/foo/bar").not_to be_routable
      end
 end

*spec/routing/widgets_routing_spec.rb *

it "routes a named route" do
      expect(:get => new_widget_path).
      to route_to(:controller => "widgets", :action => "new")
end
9 factory_girl 的使用

1.修改Gemfile配置

group :development, :test do
    gem 'factory_girl_rails', '4.2.1'
    gem 'ffaker', '~> 1.21.0'
    gem 'rspec-rails', '2.13.1'
 end

并且使用 gem 'jbuilder', '1.5.0'
配置完成之后,執(zhí)行bundle install

2.在 * app/views/widgets/index.json.jbuilder * 文件中

json.widgets @widgets do |widget|
    json.id         widget.id
    json.name       widget.name
 end

在 * app/views/widgets/show.json.jbuilder*文件中

json.widget do
    json.id        @widget.id
    json.name      @widget.name
end

3.在 spec/factories/widget.rb文件中

FactoryGirl.define do
  factory :widget do
    name { Faker::Name.name }
  end
end

4.在* spec/requests/widgets_spec.rb *文件中

describe "Widgets" do
  describe "GET /widgets" do
    let(:widgets)  { FactoryGirl.create_list(:widget, 10) }
    let(:url)      { "/widgets.json" }
    before do
      WidgetsController.stub(:index).and_return(widgets)
      get(url)
    end
    describe "return JSON" do
      it { MultiJson.decode(response.body)["widgets"].should   have_at_most(10).items }
      it "should be correct" do
        expect(MultiJson.decode(response.body)["widgets"].first["id"]).to      eq(widgets.first.id)
        expect(MultiJson.decode(response.body)["widgets"].first["name"]).to    eq(widgets.first.name)
      end
    end
  end
  describe "specify widget" do
    let(:widget) { FactoryGirl.create(:widget) }
    let(:url) {"/widgets/#{widget.id}.json"}
    before { get(url) }
    it "should be correct" do
      expect(MultiJson.decode(response.body)["widget"]).to         include("id")
      expect(MultiJson.decode(response.body)["widget"]).to         include("name")
    end
    it "should render the deal in JSON format" do
      expect(MultiJson.decode(response.body)["widget"]["id"]).to     eq(widget.id)
      expect(MultiJson.decode(response.body)["widget"]["name"]).to   eq(widget.name)
    end
  end
end

5.在spec/spec_helper.rb文件中配置如下
每次在測試中,都必須使用FactoryGirl.create(xxx) FactoryGirl.create_list(xxx)
在Factory Girl 3.0 開始, 只要做一個(gè)設(shè)置,Rails就會(huì)變得簡潔,美好
可以加在spec_helper.rb文件Rspec.configure塊中的任何地方

RSpec.configure do |config|
  config.include FactoryGirl::Syntax::Methods
end

然后在測試的代碼中,就能使用比較簡潔的語法create(xxx) create_list(xxx)
還能使用attributes_for(xxx)等

6.Factory生成文件存放的位置

通過Factory Girl生成器生成的文件都會(huì)放在 spec/factories文件夾
中,文件的名字會(huì)試用對應(yīng)模型名字的復(fù)數(shù)形式(所以Contact
模型的預(yù)構(gòu)件文件是spec/factories/contacts.rb)

10 ffaker的使用

faker在用來創(chuàng)建比較真實(shí)的假數(shù)據(jù).
ffakerfaker的重寫,速度比faker塊一些.

修改Gemfile配置

require 'ffaker'

之后執(zhí)行bundle installl給應(yīng)用程序安裝ffaker gem包.
下面是常用的示例.

Faker::Name.name                       #=> "Christophe Bartell"
Faker::Internet.email                  #=> "kirsten.greenholt@isher.info"
Faker::Geolocation.lat                 #=> 38.4685363769531
Faker::Geolocation.lng                 #=> -87.888795
Faker::Address.street_address          #=> "95519 Evans Oval"
Faker::Lorem.sentence                  #=> "Aut recusandae quam cum omnis aut dolores."
Faker::Lorem.paragraph                 #=> "Earum et est qui id totam adipisci sint. In qui officia velit veritatis rerum consequuntur. Aut earum eaque velit. Numquam minima autem at."
11 mock的使用

為什么要使用mock?
系統(tǒng)總是很復(fù)雜,不同的模塊功能耦合在一起,A調(diào)用B, B調(diào)用C甚至A.但是我們在測試A
的某個(gè)方法的時(shí)候,應(yīng)該把注意力集中在A這個(gè)方法功能上,而沒有必要把這個(gè)方法中需要的其他方法
(A的或者B,C)都測試一遍,雖然這些其他方法的正確是A的這個(gè)方法正確的保證!B,C的方法應(yīng)該在各自的測試中獨(dú)立進(jìn)行.

如一個(gè)方法,讀取rss,然后解析,生成view

url = "http://forum.rccchina.com/api/posts/meetings.xml"
require 'net/http'
require 'uri'
xml = Net::HTTP.get_print URI.parse(url)

這段代碼核心在后面解析生成view的功能,測試的時(shí)候,完全不需要真的去從forum.rccchina.com上面獲取xml的數(shù)據(jù),覆蓋Net::HTTP.get_print方法即可。

xml = "<meetings>...</meetings>"
Net::HTTP.stub!(:get_print).and_return(xml)

下面是一個(gè)mock的例子

`spec/factories/widget.rb`

FactoryGirl.define do
  factory :widget do
    name  { Faker::Name.name }
    email { Faker::Internet.email }
    tel   { 123456 }

    factory :invalid_widget do
      name  nil
      email nil
    end
  end
end

`spec/first/mock_spec.rb`

require 'spec_helper'

class Hello
  def say
    "hello world"
  end
end

describe Hello do
  context "factory girl spec " do
    let(:widget) { FactoryGirl.create(:widget) }

    subject { widget }

    it { should respond_to(:name) }
    it { should respond_to(:email) }
    it { should respond_to(:tel) }
  end

  context "mock saying hello " do
    before(:each) do
      @hello = mock(Hello)
      @hello.stub!(:say).and_return("hello world")
      @hello.stub!(:sleep).and_return("sleep")
    end

    subject { @hello }

    it { should respond_to(:say) }
    it { should respond_to(:sleep) }
  end


  context "saying hello" do
    before(:each) do
      @hello = mock(Hello)
      @hello.stub!(:say).and_return("hello world")
      @hello.stub!(:sleep).and_return("sleep")
    end


    it "#sleep should return sleep" do
      @hello.should_receive(:sleep).and_return("sleep")
      answer = @hello.sleep
      answer.should match("sleep")
    end


    it "#say should return hello world" do
      @hello.should_receive(:say).and_return("hello world")
      answer = @hello.say
      answer.should match("hello world")
    end

    it "#say should not return zzz"  do
      @hello.should_receive(:say).and_return("hello world")
      answer = @hello.say
      answer.should_not match("zzz")
    end
  end
end

從上述的例子中可以看出,FactoryGirl 是根據(jù)模型來模擬真實(shí)的字段屬性.
而mock可以模擬出模型中不存在的字段屬性.mock可以用來虛擬比較復(fù)雜的屬性

12 shared_examples 的使用

shared_examples 解決在測試用需要復(fù)用的代碼

包含shared groups 的文件必須在使用前首先被加載,

一種方法是把所有包含shared examples 的文件放到 spec/support目錄下
并且在spec/spec_helper.rb中導(dǎo)入他們
Dir["./spec/support/**/*.rb"].each { |f| require f }

spec/helpers/shared_examples_spec.rb

shared_examples 'a collection' do
  let(:collection) { described_class.new([7,2,4]) }

  context 'initialized with 3 items' do
    it "says it has three items" do
      expect(collection.size).to    eq 3
    end
  end

  describe "#include?" do
    context 'with an item that is in the collection' do
      it "returns true" do
        expect(collection).to      include(7)
      end
    end

    context "with an item that is not in the collection" do
      it "returns false" do
        expect(collection).not_to  include(9)
      end
    end
  end
end


describe Array do
  it_behaves_like 'a collection'
end

describe Set do
  it_behaves_like 'a collection'
end

spec/helpers/shared_examples_block_spec.rb

shared_examples 'a collection' do

  context 'initialized with 3 items' do
    it "says it has three items" do
      expect(collection.size).to    eq 3
    end
  end

  describe "#include?" do
    context 'with an item that is in the collection' do
      it "returns true" do
        expect(collection).to      include(7)
      end
    end

    context "with an item that is not in the collection" do
      it "returns false" do
        expect(collection).not_to  include(9)
      end
    end
  end
end


describe Array do
  it_behaves_like 'a collection' do
    let(:collection) { Array.new([7,2,4]) }
  end
end

describe Set do
  it_behaves_like 'a collection' do
    let(:collection) { Set.new([7,2,4]) }
  end
end

spec/helpers/shared_examples_block_params_spec.rb

require 'set'

shared_examples "a measurable object" do |measurement, measurement_methods|
  measurement_methods.each do |measurement_method|
    it "should return #{measurement} from ##{measurement_method}" do
      subject.send(measurement_method).should == measurement
    end
  end
end

describe Array, "with 3 items" do
  subject { [1,2,3] }
  it_should_behave_like "a measurable object", 3, [:size, :length]
end

describe String , "of 6 characters" do
  subject { "FooBar" }
  it_should_behave_like "a measurable object", 6, [:size, :length]
end
13 注意

1

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

ruby代碼中的寫法
@user.respond_to?(:name)

在RSpec中可以寫成

it "should respond to 'name'"  do
  expect(@user).to respond_to(:name)
end

因?yàn)橹付藄ubject{ @user } 我們還可以寫成單行的形式

it { should respond_to(:name) }

2

require 'spec_helper'
describe Contact do
  describe "filter last name by letter" do
    context "matching letters" do

    end

    context "non-matching letters" do

    end
   end
end

嚴(yán)格上來說describe 和 context 是可以互換的
但是describe 用來表示需要實(shí)現(xiàn)的功能,而context針對該功能不同的情況。

3
2012年6月,Rspec開發(fā)團(tuán)隊(duì)宣布,在v2.11中使用了新句法
來替代傳統(tǒng)的should式句法,如

it "is true when true" do
 true.should be_true
end

新句法會(huì)把要測試的值傳遞給 expect() 方法,然后和匹配器比較:
it "is true when true" do
 expect(true).to be_true
end

4

describe "Home page" do
  before { visit root_path }

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

  it "should have the base title" do
    expect(page).to have_title("Ruby on Rails Tutorial Sample App")
  end

  it "should not have a custom page title" do
    expect(page).not_to have_title('| Home')
  end
end

使用最新的rspec特性, 將重復(fù)的東西刪除掉

在代碼中的每一個(gè)用例都出現(xiàn)下面的東西
it "should have the content 'Sample App'" do
同時(shí)還使用了
expect(page).to have_content('Sample App')

二者雖然形式不同,要表達(dá)的意思卻是相同的.而且兩個(gè)用例都引用了page變量
使用it方法的另一種形式,把測試代碼和描述文本合二為一
我們可以告訴Rspec page就是要測試的對象(subject), 這樣就可以避免多次使用page

結(jié)果修改變?yōu)?

subject { page }
describe "Home page" do
    before { visit root_path }

    it { should have_content('Sample App') }
    it { should have_content('Ruby ...')}
    it { shoule have_content('| Home') }
end

參考:
http://ibugs.github.com/rspec_test
http://www.relishapp.com/rspec
https://github.com/rspec/rspec-expectations
https://github.com/thoughtbot/factory_girl
http://betterspecs.org/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 加速測試的方法 這里所說的“速度”有兩層含義。 其一,當(dāng)然是測試運(yùn)行所用的時(shí)間。我們這個(gè)小程序的測試已經(jīng)開始出現(xiàn)慢...
    AQ王浩閱讀 2,641評論 1 9
  • 加速測試的方法 這里所說的“速度”有兩層含義。 其一,當(dāng)然是測試運(yùn)行所用的時(shí)間。我們這個(gè)小程序的測試已經(jīng)開始出現(xiàn)慢...
    AQ王浩閱讀 1,031評論 0 1
  • 話說昨日,健哥讓我分享下怎么用rspec寫模型的測試,頓時(shí)一臉懵逼,因?yàn)橹粫?huì)些拳腳貓功夫,趕緊百度谷歌相關(guān)知識,七...
    嚴(yán)三金閱讀 4,570評論 2 52
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,647評論 19 139
  • Awesome Ruby Toolbox Awesome A collection of awesome Ruby...
    debbbbie閱讀 3,089評論 0 3

友情鏈接更多精彩內(nèi)容