使用 Moq 測試.NET Core 應用 -- Mock 方法


第一篇文章, 關於Mock的概念介紹: https://www.cnblogs.com/cgzl/p/9294431.html

本文介紹使用Moq來Mock方法.

使用的代碼: https://github.com/solenovex/Moq4-Tutorial-Code 里面的 02 Before 部分.

Mock 對象

緊接着上文中的例子. 上一篇文章, 我在單元測試的時候, 把依賴項設為null:

然后便出現了NullReferenceException, 導致測試無法正常運行.

首先應該做的是在TransferApproval的構造函數里判斷參數是否為null, 如果為null的話應該拋出ArgumentNullException:

這是更恰當的異常.

這樣的話, 在測試的時候, 拋出的就是ArgumentNullException了, 它可以更恰當的表達程序出現的問題:

 

現在我們可以使用mock版本的依賴項來代替null了:

上面的代碼首先使用Moq創建了一個mock版本的IPhysicalExamination的實例.

而由於Moq對依賴項進行了包裝, 所以要獲得實際的mock依賴項, 我們需要使用mockExamination.Object屬性. 而這個屬性的類型就是IPhysicalExamination.

 

另外一個測試方法我也這么改一下, 然乎重新Build. Run All Tests:

還是紅色的, 但現在是測試沒通過, 並不是拋出異常.

測試沒通過的意思就是期待值和實際返回值不符.

 

讓我們來調試一下這個測試, 我在TransferApproval類里面設置一個端點, 查看一下這個mock依賴項的方法返回值:

 

然后調試測試:

跑到斷點

可以看到這個Mock版本依賴項的IsHealthy()方法的返回值是false.

我並沒有對這個Mock版本的IPhysicalExamination的IsHealthy()方法設定返回值, 正因為如此, 它才會返回它方法返回類型的默認值, 它的返回類型是bool, 而bool的默認值是false, 所以現在IsHealthy()方法在沒有設定的情況下的返回值就是false.

 

It類

而PhysicalExamination這個具體的實現類由於各種原因導致還沒有實現, 為了讓它不妨礙我們的單元測試, 我先設定讓它在無論傳進什么參數的情況下都會返回true.

從業務上來講就是假設所有轉會球員都可以通過體檢:

那么現在所有的測試都應該可以通過了:

 

這里用到了It這個類, 在Moq里, It這個類是用來做參數匹配的, it 就是"它"的意思, 它就代表需要被匹配的參數. 

It.IsAny<T>(), 它表示傳遞給方法的參數的類型只要是T就可以, 值是任意的. 只要滿足了這個條件, 那么方法的返回值就是后邊Returns()方法里設定的值.

 

Moq 關於It類的文檔: http://www.nudoq.org/#!/Packages/Moq/Moq/It

它有下面幾種用法:

  • Is<TValue>(Expression<Func<TValue, Boolean>>)
  • IsAny<TValue>()
  • IsIn<TValue>(IEnumerable<TValue>)
  • IsInRange<TValue>(TValue, TValue, Range)
  • IsNotIn<TValue>(IEnumerable<TValue>)
  • IsNotNull<TValue>()
  • IsRegex(string)

我認為通過方法名就可以知道這些方法的用途.

下面我修改一下該測試方法, 使用It其它幾個方法:

其測試結果仍然是通過的.

 

嚴謹(Strict) vs 寬松(Loose) Mock

Moq里面有Strict(嚴謹)和Loose(寬松) mock對象的概念, 當然也有很多人不喜歡這個概念.

在當前的測試方法里, TransferApproval依賴於Mock<IPhysicalExamination>, 並調用其IsHealthy()方法.

如果不對IsHealthy()方法進行任何設定的情況下, 方法會返回bool的默認值false, 這種就是loose(寬松) Mock.

 

在創建Mock對象的時候, 還可選傳遞一個MockBehavior這個參數.

MockBehavior是一個枚舉, 它有三個值:

  • MockBehavior.Strict, 如果mock對象上的方法沒有被預先設置好, 那么測試中調用該方法的時候就會拋出異常.
  • MockBehavior.Loose, 即使方法沒有被預先設置, 調用它的時候也不會拋出異常. 它會返回該方法返回類型的默認值.
  • MockBehavior.Default, 它代表MockBehavior.Loose.

 

如果上例使用Strict Mock, 那么將會拋出Exception:

 

下面我把一個測試改為Strict Mock, 並取消了對IsHealthy()方法的設置:

 

而測試時會拋出MockException:

 

在對方法進行設置后, 測試就會通過:

 

可以感覺到:

Loose Mock, 可以少寫一些設定代碼, 可以返回默認值, 不易讓測試中斷

Strict Mock, 需要寫跟多的設定代碼, 每個被調用的方法都需要進行設定, 所以也更容易讓測試中斷.

 

Moq的建議是: 大多數情況下應該使用Loose Mock, 只有特殊需要的時候才去使用Strict Mock.

 

out參數

修改一下TransferApproval類的轉會審批方法:

這次使用的是帶有out參數的IsHealthy()方法.

 

建立一個測試方法, 並設定這個帶有out參數的方法:

很簡單, 測試會通過:

 

完成的代碼在: https://github.com/solenovex/Moq4-Tutorial-Code 02 After

未完待續....

 


免責聲明!

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



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