使用 Moq 測試.NET Core 應用 - Why Moq?


 什么是Mock

當對代碼進行測試的時候, 我們經常需要用到一些模擬(mock)技術.

綠色的是需要被測試的類, 黃色是它的依賴項, 灰色的無關的類

 

在一個項目里, 我們經常需要把某一部分程序獨立出來以便我們可以對這部分進行測試. 這就要求我們不要考慮項目其余部分的復雜性, 我們只想關注需要被測試的那部分. 這里就需要用到模擬(Mock)技術.

因為, 請仔細看. 我們想要隔離測試的這部分代碼對外部有一個或者多個依賴. 所以編寫測試代碼的時候, 我們需要提供這些依賴. 而針對隔離測試, 並不應該使用生產時用的依賴項, 所以我們使用模擬版本的依賴項, 這些模擬版依賴項只能用於測試時, 它們會使隔離更加容易.

綠色的是需要被測試的類, 黃色Mock的依賴項

 

Mock技術帶來的優點

使用Mock技術, 可以有如下的優點:

  • 提高測試運行速度, 例如可以模擬DB, Web Service等比較慢的服務, 以及算法等.
  • 支持並行開發, 例如實際的依賴項還沒有完成開發, 或者等待其他團隊開發依賴項.
  • 提高測試可靠性, 例如有時這個依賴項的bug太多了, 經常由於依賴項的原因導致測試失敗, 那么就應該使用mock版本來驗證我們自己寫的代碼.
  • 減少開發/測試成本, 有時程序可能依賴一些雲服務, 這些服務是按調用次數收費的, 那么就可以使用Mock版本來節省這方面的開資, 當然了最后還是需要使用真正的服務測試才行; 有時候組建依賴項太費勁了, 就用mock版本吧, 省時省力.
  • 在有不確定性依賴項的情況下進行測試, 有些依賴項有不確定性, 可能無理由的造成測試失敗, 這時候就應該使用mock版本的依賴.

 

單元測試

Mock技術通常在單元測試中使用, 可以使用xUnit來為.NET Core應用做單元測試, 這里有介紹xUnit的文章: https://www.cnblogs.com/cgzl/p/9178672.html#xunit

那么什么是一個單元? 

這個通常是由團隊對系統的理解決定, 可以針對一個類, 也可以針對多個類.

單元測試通常具有以下特點:

  • 低級別
  • 高聚焦
  • 執行速度快
  • 容易測試所有執行路徑上的代碼

 

術語

  • Test Double (我認為可以翻譯為測試替身), 是所有非真實依賴項的總稱.
    • Fake, Fake是那種可以正常工作的實現, 盡管可以正常工作, 但是它們不可以用於生產環境, 例如EFCore里的內存數據庫提供商.
    • Dummy, 有時候, 被測試方法需要一些參數, 但是這些參數實際上並沒有用到, 這時就可以創建dummy, 它們的存在只是為了滿足調用方法的參數要求.
    • Stub, (狀態測試). 它可以使用很直接的方式模擬依賴項的行為. 例如我們可以使用Stub把相關數據放到內存里查詢而不是查詢真實的數據庫; 如果某個測試類需要依賴項的某個Property的值, 那么stub就設定這個值就行.
    • Mock, (行為/交互測試). 與Stub不同的是, Mock期待的不是返回值, Mock期待的是動作的執行. 它是依賴項的動態包裝, 它可以對哪個方法以什么樣的順序被待測試系統(SUT)調用的這個期待行為進行預編程. 也就是說被測試的系統只有按照特定的順序調用mock依賴項的特定方法, 那么該系統才算測試通過.

還有其它的一些術語就不介紹了, 主要是這四個.

對於Stub 和 Mock ,可以看下面兩張圖例:

 

Moq

官網: https://github.com/moq/moq4

Moq框架可以用來創建dummy, stub 和 mock. 在本文里把這三個東西都叫做mock對象吧.

Moq使用一套API來創建stub和mock對象.

 

准備項目

一個簡單的.NET Core控制台項目: https://github.com/solenovex/Moq-Tutorial-Code, 代碼是里面的01 before.

該項目非常簡單, 是關於球員轉會業務, 它目前只有三個類. 

TransferApplication, 球員轉會申請類:

TransferResult, 轉會審批結果枚舉:

 

還有TransferApproval, 轉會審批類:

'

當前的邏輯是, 發起球員轉會申請后, 進行審批: 如果總費用大於預算, 那么就直接拒絕; 如果總費用不超標, 並且球員小於30歲, 那么就批准; 但如果球員大於30歲, 並且是超級巨星的話, 這將由老板決定.

 

建立單元測試項目

在解決方案里建立一個xUnit類型的項目:

 

然后要保證該項目所用到的庫都保持最新:

 

最后別忘了添加對FootballManager項目的引用:

 

打開Text Explorer, 可以看到里面有一個待測的單元測試:

 

做一個簡單的單元測試

把UnitTest1改成下面這個簡單的單元測試:

重新Build后, 可以看到單元測試的名稱更新了.

 

點擊Run All, 運行單元測試, 結果成功:

 

隨后再添加一個簡單的單元測試:

 

Build, 后就會出現這個測試:

 

Run All, 測試也會成功:

 

添加依賴

這時, 有一些需求的變化, 球員轉會審批前, 需要通過體檢.

首先在轉會申請類里面添加兩個球員的屬性:

 

然后添加一個體檢的接口:

這兩個方法的作用是一樣的, 但是調用方法略有不同.

 

但是此時, 該接口的實現類還沒有開發完畢:

 

在轉會審批類里面, 需要添加這個依賴, 使用的是接口:

 

在單元測試類里面, 我為轉會球員添加了這兩個屬性, 但是審批類會報錯, 因為沒有加入依賴項:

 

所以測試的時候需要注入這個依賴項IPhysicalExamination, 但是PhysicalExamination類還沒有做完(里面的方法都沒有實現), 所以我們無法new出來這個類.

這時, 我們也許可以傳null進去?

 

這時, 項目是不報錯了.

跑單元測試, Run All:

測試失敗, 拋出NullReferenceException. 而這個異常導致了測試無法正常進行.

 

所以, 我們需要Moq, 它可以提供一個Mock(模擬)版本的IPhysicalExamination, 並把它傳遞到審批類的構造函數里.

 

安裝Moq

在單元測試項目添加Moq:

 

Moq的第一篇先到這.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM