在我們在撰寫測試的過程中,可能因為同一類型的物件行為有著類似的情況,因此會需要重複的專寫相似的測試案例,這個時候我們就可以利用「共用案例(Shared Example)」的機制來重複利用這些測試案例。
使用方式
要定義一個共用案例,可以使用 .shared_examples
來進行,因為是 Class Method(類別方法)因此可以直接在測試中撰寫,或者另外新增一個檔案來進行定義。
1RSpec.describe 'User API', type: :api do
2 shared_examples 'successful response' do
3 it { is_expected.to be_successful }
4 end
5
6 describe 'GET /users' do
7 # ...
8 it_behaves_like 'successful response'
9 end
10
11 describe 'GET /users/:id' do
12 # ...
13 it_behaves_like 'successful response'
14 end
15end
像這樣子我們就可以在所有預期會有「成功回傳」的測試中重複使用這些共用的測試案例,要注意的是使用共用案例有以下限制。
範圍
根據我們定義共用案例時機,會讓可以引用的範圍受到不同程度的限制。基本原則是「下層可以繼承上層」的方式。
舉例來說,如果是跨 Context(情境)的狀況,是無法被共用的。
1RSpec.describe 'User API', type: :api do
2 describe 'GET /users' do
3 shared_examples 'user list' do
4 it { is_expected.to include('users' => be_a(Array)) }
5 end
6 # ...
7
8 end
9
10 describe 'GET /admin/users' do
11 # ...
12 it_behaves_like 'user list'
13 end
14end
因為 GET /users
和 GET /admin/users
是相同層級,因此會無法飲用到另一個層級的共用案例,要解決這個問題只需要向上放置即可。
也因此,如果是經常被共用的案例,我們可以直接對整個 RSpec 進行定義。
1RSpec.shared_examples 'user list' do
2 it { is_expected.to include('users' => be_a(Array)) }
3end
如此一來,我們只需要在運行測試前載入這個共用案例的定義就可以使用,而不需要在不同的測試案例中去定義。
參數
我們在前面的例子中定義了 user list
這個共用案例,但大多數 API 回應都是列表,我們可以利用參數的機制讓他變成 list resources
之類的共用案例。
1RSpec.shared_examples 'list resources' do |resource:|
2 it { is_expected.to include(resource => be_a(Array)) }
3end
在使用時,我們就可以額外的提供參數來使用。
1RSpec.describe 'User API', type: :api do
2 describe 'GET /users' do
3 # ...
4 it_behaves_like 'list resources', resource: 'users'
5 end
6
7 describe 'GET /admin/users' do
8 # ...
9 it_behaves_like 'list resources', resource: 'admin_users'
10 end
11end
像這樣,我們就可以提供不同類型的 List API 來使用這個共用案例,而不需要重複定義多次。
除此之外,我們也可以傳入一個 Block 在共用案例被執行之前呼叫,來做一些對應的處理。
1RSpec.describe 'User API', type: :api do
2 describe 'GET /users' do
3 # ...
4 it_behaves_like 'list resources', resource: 'users' do
5 before { API.adapter = MockUserAPI }
6 end
7 end
8
9 describe 'GET /admin/users' do
10 # ...
11 it_behaves_like 'list resources', resource: 'admin_users' do
12 before { API.adapter = MockAdminUserAPI }
13 end
14 end
15end
如此一來我們在共用案例上就能有非常多的彈性,來對應那類有著「類似行為」的測試。下一篇我們會介紹「自訂匹配器」並且分析跟共用案例的差異。
如果想在第一時間收到更新,歡迎訂閱弦而時習之在這系列文章更新時收到通知,如果有希望了解的知識,可以利用優雅的 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 測試 - 探索式的測試與重構