在測試中,我們經常會看到 Mock 和 Stub 這兩個詞,很多時候也不容易區分清楚。在 RSpec 中我們基本上不會看到 Mock 或者 Stub 這兩個詞,取而代之的是 Double 和 Allow。
Mock
當我們希望某個物件會做出我們「預期」的反應,那麼就可以使用 Mock 的方式來進行處理,通常會是以依賴注入(Dependency Injection)的方式被使用。
舉例來說,假設我們有一個物件會統計合法使用者的數量,但我們只希望驗證「統計」這件事情,就可以對使用者查詢的來源進行 Mock 的處理。
1class TeamCreator
2 def initialize(user_repo)
3 @user_repo = user_repo
4 end
5
6 def valid_user_count
7 @user_repo
8 .in_team
9 .select(&:valid?)
10 .size
11 end
12
13 # ...
14end
針對這個測試,我們可以製作 User Repository 的替身來回傳我們預期的「正確」使用者數量。
1RSpec.describe TeamCreator do
2 subject(:creator) { TeamCreator.new(user_repo)}
3
4 let(:user_repo) { ... }
5 # ...
6
7 describe '#valid_user_count' do
8 subject { creator.valid_user_count(team: team) }
9
10 let(:team) { create(:team) }
11 let(:user_repo) do
12 instance_double(
13 UserRepository,
14 where: build_list(:user, 3, :valid)
15 )
16 end
17
18 it { is_expected.to eq(3) }
19 end
20end
像這樣,我們可以讓 UserRepository 的 #where
方法直接回傳我們預期的內容,這樣就可以在不在資料庫實際建立資料的狀況下通過測試。
Stub
跟 Mock 不同的是,Stub 只會對「一部分物件」產生改變,也因此我們經常被用來去設定要回傳的測試資料。
我們可以調整上面的測試,改為下面這樣的形式。
1RSpec.describe TeamCreator do
2 subject(:creator) { TeamCreator.new(user_repo)}
3
4 let(:user_repo) { ... }
5 # ...
6
7 describe '#valid_user_count' do
8 subject { creator.valid_user_count(team: team) }
9
10 let(:team) { create(:team) }
11 let(:user_repo) { instance_double(UserRepository) }
12
13 before do
14 allow(user_repo).to receive(:where).with(team: team).and_return(build_list(:user, 3, :valid))
15 end
16
17 it { is_expected.to eq(3) }
18 end
19end
看起來只是將 #where
的定義從 instance_double
移出來,改為使用 #allow
來定義,這樣的差異在哪裡?
基本上是沒有差異的,更多的時候我們應該採用 Stub 的方式來處理這類情況會更好,因為我們可以指定該收到怎樣的參數。
這其實是 Ruby 的語言特性所造成的,因為我們很容易可以「抽換」物件的實作,也因此大多數時候不太需要製作 Mock 的物件,而是直接對物件本身抽換就能夠處理大多數的情況。
然而 instance_double
還是非常有用的,我們會在後續詳細的討論這些應用情境。
如果想在第一時間收到更新,歡迎訂閱弦而時習之在這系列文章更新時收到通知,如果有希望了解的知識,可以利用優雅的 RSpec 測試回饋表單告訴我。
如果對這篇文章有興趣,可以透過以下連結繼續閱讀這系列的其他文章。
- 優雅的 RSpec 測試 - 前言
- 優雅的 RSpec 測試 - 撰寫測試的方式
- 優雅的 RSpec 測試 - RSpec 概觀
- 優雅的 RSpec 測試 - 測試案例
- 優雅的 RSpec 測試 - 組織測試
- 優雅的 RSpec 測試 - 前置處理
- 優雅的 RSpec 測試 - 常見匹配器
- 優雅的 RSpec 測試 - 內容匹配
- 優雅的 RSpec 測試 - 錯誤匹配
- 優雅的 RSpec 測試 - 共用案例
- 優雅的 RSpec 測試 - 自訂匹配器
- 優雅的 RSpec 測試 - 輔助方法
- 優雅的 RSpec 測試 - 測試替身
- 優雅的 RSpec 測試 - Mock 與 Stub
- 優雅的 RSpec 測試 - Allow 的使用方式
- 優雅的 RSpec 測試 - Allow 變化應用
- 優雅的 RSpec 測試 - Spy 的應用
- 優雅的 RSpec 測試 - 物件的可測試性
- 優雅的 RSpec 測試 - 耦合與依賴注入
- 優雅的 RSpec 測試 - 探索式的測試與重構