Mock、Stub 與 Double
2024-09-21 16:02:49
## 前言
寫測試這件事情,2022 年在 [WeHelp](https://wehelp.tw/) 轉職訓練時是全然不了解。
進了公司後還記得很快就聽到前輩們分享寫測試的重要性,以及後端被要求一定要寫測試來保護你的程式碼,所以就不得不逼自己了解怎麼寫測試了。
不過這篇不是要介紹「[單元測試(Unit Test)](https://aws.amazon.com/tw/what-is/unit-testing/)」而是是因為開始工作到現在,
都搞不太懂這 3 個名詞,花了點時間研究後做點整理,算是做筆記也看能不能幫助想了解的人用較簡單的方式理解,
以下舉例會用 [Ruby on Rails](https://rubyonrails.org/) 的 [RSpec](https://rspec.info/) 做說明。
## Mock
用來驗證程式碼是否執行了預期的操作,通常應用在測試流程中會確定特定動作是否發生。
簡言之不僅模擬方法的行為,還會檢查這個方法是否在測試中被呼叫,並且可以驗證呼叫的次數、參數等。
寫測試很直覺的主要應該都在做這件事。
以下我們「預期」 `User.find(1)` 一定會被呼叫,並且會回傳 `user` 物件,即找到 `id` 為 1 的 `User`。
如果這個方法沒有被呼叫,測試就會失敗。
```ruby
expect(User).to receive(:find).with(1).and_return(user)
```
## Stub
**為了減少測試的依賴性**。
當一個方法或模組依賴外部系統(如 API、資料庫查詢等)時,為了避免測試過程受這些依賴影響,可以用 stub 來模擬行為。
白話點就是模擬一個物件的方法,且我們不關心這個方法的具體行為,只是需要一個預設的回傳值時,就會使用 stub。
以下我們定義當 `User.find(1)` 被呼叫時,會返回 `user` 物件,而不會真的去資料庫查詢。
```ruby
allow(User).to receive(:find).with(1).and_return(user)
```
## Double
用於提供測試中的靈活性和避免依賴真實物件,尤其是在被測試物件有複雜邏輯或需要多次呼叫不同方法時。
即一個模擬物件包含測試所需的屬性,用來替代實際的物件,並允許你定義多個方法的 stub 行為。
以下 `user_double` 是一個模擬的 `User` 物件,當呼叫 `name` 或 `age` 時,會回傳對應的值;呼叫 `greet` 時會回傳 `'Hello'`。
```ruby
user_double = double('User', name: 'RJ', age: 18)
allow(user_double).to receive(:greet).and_return('Hello')
```
## 結論
這三者的使用有助於在開發過程中維持測試的「可預測性」和「獨立性」。
有效地使用這些技術,能夠讓測試更加專注於邏輯本身而非外部依賴,讓測試變得更可靠、更快速。
此概念通用於所有語言和框架的測試中。
- Mock:驗證某個動作是否如設計般被執行,關注的是**_行為是否發生_**。
- Stub:提供假資料或行為,用以**_代替測試中不需要真實執行的過程或外部依賴_**。
- Double:完全模擬一個物件(object),包括屬性(attribute)和方法(method),以**_完全取代測試中的真實物件_**。
> 不過我目前為止印象中很少用到 double...,原因我也不清楚 XDD
---
#### 本文章同步分享於我的部落格 [RJ Wanna Say Something](https://ssuj-chang.github.io/post/mock_stub_double/)
點擊複製文章連結
X