開發的過程要寫程式,寫程式我們都知道要寫測試,寫測試的時候經常看見 Stub, Dummy, Mock 等等這些名詞。使用 Testing Framework 的時候,更是經常使用到看起來功能類似,不同名稱的物件。稍微理解這些名詞的概念,使用測試框架的時候會有點幫助。

首先,這是看了 xUnit Patterns 網站裡面的部分頁面,寫下的筆記。如果有看不懂的地方,請連回去直接看原文,或是用這當關鍵字之一去搜尋其他人的文章。

接著來一些會反覆出現的名詞解釋

名詞

  • SUT
    System Under Test,就是待測物。舉例來說,當我們在寫 Unit Test,SUT 往往就是 Class,那個我們要測試的 Class

  • DOC
    Depended-On Component,SUT 所依賴的元件,也就是 Mock, Stub ..想要抽換掉的元件。把這些相依性抽換成我們可以控制的狀態,以專注去測試 SUT。

  • test fixture
    測試的時候特意準備的東西,讓測試可以順利跑完所需要,有些人會叫做 test context。舉例來說,一組測試用的資料。

  • control point
    用 control point 來操作 SUT,有些 control point 只是為了測試而存在。舉例來說,某個 class 開了純為測試存在的 API,這種 control point 當然不能在產品裡面使用。

  • indirect input
    改變 SUT 的數值,並非直接來自我們的操作,而是來自於 DOC。好比我們對一個物件呼叫 updateName,然後物件去問另外一個資料庫物件,取得了在資料庫裡面的數值來更新自己

  • indirect output
    當 SUT 的一些狀態改變了,而我們沒有辦法透過直接的 API 去取得改變的狀態。可是,它同時也會影響到其他元件的狀態,後面這些能被觀察的狀態,就是 indirect output。這與後面的 Mock object 有關。

  • Test double

    本文的重點,我看的書有將其翻譯為「測試副本」。這個字不好翻譯,double 當作名詞有「替身」的意思,Test Double 就是我們要拿來替換掉真實運作的 DOC 的物件們,得到一個更簡單,可以測試的環境。

    大致能夠把 Test double 分成以下幾種

測試的過程,可以用這張圖大概解釋一下

  1. Setup,生成 Test double 來取代 DOC,並準備好 Fixture 方便接下來的測試
  2. 初始化 SUT,通常就是建構一個 object
  3. Exercise,執行測試。這時候 SUB 還可能會去跟 Test double 去要一些數值
  4. Verify,確認執行完的結果是否無誤
  5. Teardown,把之前測試過程產生的東西給清乾淨

有了這個流程,接下來要解釋 Test double 就簡單多了

Test Stub

Test stub 就是,提供 indirect input 給 SUT 的 Test double

舉例來說,我們要測試登入用的程式是不是有正常運作。SUT 就是登入用的主程式,塞了一個假的 Network 物件給他,只要對 Network 物件下 GET 總是回傳 200 OK。這個 Network 物件就是 Stub。

Test Spy

Test spy 就是能力更強,有記錄功能的 Test Stub,提供 indirect input 給 SUT 的 Test double

以前面的例子來說,在驗證的階段,可能還想要知道這個 Network 物件的 API 被呼叫了幾次,有沒有使用到錯的 end-point?能夠告訴我們這些資訊的,就是 Test Spy。

Mock Object

Mock Object 就是帶有判斷功能的 Stub,判斷 SUT 是不是正確地使用這個 DOC

前面的 Spy 是看最後有沒有正確的 indirect output。但也有可能,最後的結果是對的,中間的順序錯了。好比說呼叫 web api 的順序是不是正確的?這時候我們需要在 Test double 裡面偷看實作的運作。Mock Object 就會在被使用的過程中,擁有檢查的邏輯。

Fake Object

Fake Object 就是提供比較簡單、輕量實作的 DOC

Fake Object 不在意 Indirect input 也不在意 indirect output,只專心地滿足自己的介面。

前述的幾個物件,要嘛是專門提供假的固定值(Stub),要嘛是有檢查的功能。Fake 物件就是一個簡化過 DOC,有著一樣的介面但是實作都很簡單,譬如說是個 In-memory 的資料庫物件,用起來像是真正的資料庫,但是操作沒有 disk IO,而且資料可能是我們寫好的 fixture。

Dummy Object

用來填充的無用物件,只是為了滿足 API 的介面

建構一個物件,或是呼叫一個方法的時候,有時候需要傳入一些物件當作參數,而我們知道這些物件完全不會被使用到,但是為了要滿足介面,成功編譯,塞進去的填充物就是 Dummy Object。

在 Debian 上 dm-crypt 的緊急救援 ← Prev Next → Kotlin 的 scope function: apply, let, run..等等