10 months ago

Mocking

在這個例子裡面,我們用了 mock 手法,確認 show 這個 action,真的有去呼叫 Suggestion.find

  • allow(Suggestion).to receive(:find).and_return(suggestion)
  • expect(Suggestion).to receive(:find)
  describe "GET show" do

    it "assigns @course and render show" do
      suggestion = double(:to_param => "123" )
      allow(Suggestion).to receive(:find).and_return(suggestion)
      expect(Suggestion).to receive(:find)

      get :show, :id => suggestion.to_param

      expect(response).to render_template :show
      expect(assigns[:suggestion]).to eq(suggestion)

    end

  end
class SuggestionController
  def show
    @suggestion = Suggestion.find(params[:id])
  end
end

劣勢:順序「反直覺」

但是其實這樣的寫法,對於我們之前提到的 Four-Phase Test:

  • Setup (準備測試資料)
  • Exercie (實際執行測試)
  • Verification (驗證測試結果)
  • Teardown (拆解測試)

有點相違背了。

我們先做了 Verification (expect)再做 Exercise ( get :show) 。

劣勢:容易失敗

而且在拆解階段,我們會將這段測試重寫成這樣。將 expect 寫在 before 階段,但這樣寫的缺點是,expect 如果不如預期,後面測試會因為 except 全死。

  describe "GET show" do

    before do 
      @suggestion = double(:to_param => "123" )
      allow(Suggestion).to receive(:find).and_return(@suggestion)
      expect(Suggestion).to receive(:find)
      get :show, :id => @suggestion.to_param
    end

    it { expect(response).to render_template :show }
    it { expect(assigns[:suggestion]).to eq(@suggestion)}
  end

Spying

與其使用 Mocking 手法,我們可以改用 Spy 手法,將測試改成這樣。(RSpec 使用 have_received 去驗證)

  describe "GET show" do

    before do 
      @suggestion = double(:to_param => "123" )
      allow(Suggestion).to receive(:find).with(@suggestion.to_param).and_return(@suggestion)
      get :show, :id => @suggestion.to_param
    end

    it { expect(response).to render_template :show }
    it "should find suggestion and assign suggestion" do 
       expect(Suggestion).to have_received(:find).with(@suggestion.to_param)
       expect(assigns[:suggestion]).to eq(@suggestion)
    end

  end

Four Phase 順序不但正確,而且是在 Exercise 後,才做 Verification。

← [RSpec] 進階測試系列概念 - Part 5 Stubbing 我是如何學一門新技術的 →
 
comments powered by Disqus