Stubbing
double
可以讓我們輕易地閃掉準備「協作者」的痛苦。但是有時候,我們還是可能被「協作者」本身的內部邏輯改變整到。
比如說,這裡是一個「建議」Suggestion 表單:
require "rails_helper"
RSpec.describe SuggestionsController, type: :controller do
describe "POST create" do
context "when suggestion is invalid" do
it "render new" do
post :create, :suggestion => { :title => "", :description => "" }
expect(response).to render_template :new
end
end
end
end
class Suggestion < ActiveRecord::Base
validates :title, presence: true
end
class SuggestionsController
def create
@suggestion = Suggestion.new(suggestion_params)
if @suggestion.save
redirect_to root_path
else
render :new
end
end
protected
def suggestion_params
params.require(:suggestion).permit(:title, :description)
end
end
如果物件非法的話,要 render new
這個 action。正常來說,這樣的 controller 測試,應該會通過。但是有時候 controller 程式碼沒有動,但 model 程式碼內部動了,比如說在這個例子,可能多加了新的欄位,或者是新增了其他的驗證方式,導致這個 controller 測試要因為 suggestion 內部本身的邏輯要修改。
這樣真的很討厭。
重寫測試
其實這裡我們根本不應該塞「例子」進去,這樣 suggestion 內部邏輯一旦改變,我們的 controller test 就要改個沒完。我們真正應該要做的是:
- 準備一個「一定儲存失敗的替身」
- 然後在 controller 讓
@suggestion.save
鐵定失敗 - 因為我們要驗證的是 「
render :new
」
所以我們可以把測試改寫成這樣
require "rails_helper"
RSpec.describe SuggestionsController, type: :controller do
describe "POST create" do
context "when suggestion is invalid" do
it "render new" do
invalid_suggestion = double(:save => false)
allow(Suggestion).to receive(:new).and_return(invalid_suggestion)
post :create, :suggestion => { :xxx => "yyy" }
expect(response).to render_template :new
end
end
end
end
這樣 suggestion 內部驗證再怎麼樣變,都不會影響到這個 controller test。