文章目錄
如何 Mock Private Method 的回傳值 - 使用 JuskMock
前一篇筆記 使用 Moq 來 Mock protected Method 文末心得中提到傳言中付費的 mock framewrok 號稱無論是什麼狀況都能 mock,想說改天要找個機會來測試一下,結果自己忍不住好奇心,立馬測試了起來 XD
就來看看該如何使用 JustMock 來 mock private method 吧
關於 JustMock
這是 Telerik 出品的 mock framework,Telerik 有多項 UI 相關產品,使用的人也不少,尤其是在 Bootstrap 還沒有那麼風行的年代,工程師普遍對於 UI 苦手,Telerik 的產品著實解救了許多被客戶嫌棄沒有美感的工程師。如果沒參與到那個年代,也許用過或是聽過一套免費的網路封包截錄工具 - Fiddler ,這也是 Telerik 的產品
至於 JustMock 的特色,請直接參考官網介紹 - JustMock,功能非常強大,缺點是它必需要付費使用,好消息是它提供免費 30 天的試用可以拿來測試
JustMock 有提供 JustMock Lite 的 open source 免費版,功能少了很多(最需要的 non-public 就不支援),官網上有提供功能比較,詳細內容請參考 JustMock Lite
基本設定
Restful Web Api 專案,Get 方法需要傳入一個 string 參數,如果未傳入 string 或傳入的參數是空字串就回傳 BadRequestResult
,傳入參數是正常 string 就回傳 OkResult
, 其中檢查 string 的邏輯與動作就獨立包裝在 detectStringEmpty
function 中,程式碼內容如下
public class ValuesController : ApiController
{
public IHttpActionResult Get(string value)
{
var isNullorEmpty= detectStringEmpty(value);
if (isNullorEmpty)
return BadRequest();
else
return Ok();
}
private bool detectStringEmpty(string value)
{
if (string.IsNullOrWhiteSpace(value))
return true;
else
return false;
}
}
以上內容皆與 使用 Moq 來 Mock protected Method 相同
撰寫測試程式
- 安裝 JustMock - 下載點
引用 namespace
using Telerik.JustMock;
啟用 JustMock
Visual Studio 主選單 JustMock –> Enable Profiler
未啟用錯誤訊息
訊息內容
Test Name: Get_StringEmpty_BadRequestResult Test FullName: UnitTestStaticField.Controllers.Tests.ValuesControllerTests.Get_StringEmpty_BadRequestResult Test Source: C:\Users\YowkoTsai\documents\visual studio 2017\Projects\UnitTestStaticField\UnitTestStaticFieldMTests\Controllers\ValuesControllerTests.cs : line 111 Test Outcome: Failed Test Duration: 0:00:00.1605095 Result StackTrace: at Telerik.JustMock.Core.ProfilerInterceptor.ThrowElevatedMockingException(MemberInfo member) at Telerik.JustMock.Core.MocksRepository.CheckMethodInterceptorAvailable(IMatcher instanceMatcher, MethodBase method) at Telerik.JustMock.Core.MocksRepository.AddArrange(IMethodMock methodMock) at Telerik.JustMock.Core.MocksRepository.Arrange[TMethodMock](Object instance, MethodBase method, Object[] arguments, Func`1 methodMockFactory) at Telerik.JustMock.Expectations.NonPublicExpectation.<>c__DisplayClass22`1.<Arrange>b__20() at Telerik.JustMock.Core.ProfilerInterceptor.GuardInternal[T](Func`1 guardedAction) at Telerik.JustMock.Expectations.NonPublicExpectation.Arrange[TReturn](Object target, String memberName, Object[] args) at UnitTestStaticField.Controllers.Tests.ValuesControllerTests.Get_StringEmpty_BadRequestResult() in C:\Users\YowkoTsai\documents\visual studio 2017\Projects\UnitTestStaticField\UnitTestStaticFieldMTests\Controllers\ValuesControllerTests.cs:line 114 Result Message: Test method UnitTestStaticField.Controllers.Tests.ValuesControllerTests.Get_StringEmpty_BadRequestResult threw exception: Telerik.JustMock.Core.ElevatedMockingException: Cannot mock 'Boolean detectStringEmpty(System.String)'. The profiler must be enabled to mock, arrange or execute the specified target.
錯誤截圖
arrange
使用 JuskMock 語法 mock 測試目標
var target = Mock.Create<ValuesController>();
使用 JuskMock 語法 mock 測試目標方法回傳值
- 重點 一:non-public 的 member 需使用
NonPublic
api - 重點 二:執行目標方法具有回傳值,Arrange 要使用泛型版本並指定回傳型別
- 重點 三:其他用法請參考官方文件 Mocking Non-public Members and Types
Mock.NonPublic.Arrange<bool>(target, "detectStringEmpty", new object[] { string.Empty }).Returns(true);
- 重點 一:non-public 的 member 需使用
通知 JuskMock 需執行原測試目標方法
如果不加上
.CallOriginal()
會執行 JustMock 建立出來的 instance 方法,結果會跟預期完全不同,要特別留意Mock.Arrange(() => target.Get(string.Empty)).CallOriginal();
定義預期結果
var expected = typeof(BadRequestResult);
act
執行測試目標程式方法
var actualAction = target.Get(string.Empty);
將實際執行結果轉型為預期的
BadRequestResult
var actual = actualAction as BadRequestResult;
assert
檢查執行結果不能是 null
Assert.IsNotNull(actual);
驗證執行結果是否與預期相符
Assert.IsInstanceOfType(actual, expected);
完整程式碼
[TestMethod()] public void Get_StringEmpty_BadRequestResult() { //arrange var target = Mock.Create<ValuesController>(); Mock.NonPublic.Arrange<bool>(target, "detectStringEmpty", new object[] { string.Empty }).Returns(true); Mock.Arrange(() => target.Get(string.Empty)).CallOriginal(); var expected = typeof(BadRequestResult); //act var actualAction = target.Get(string.Empty); var actual = actualAction as BadRequestResult; //assert Assert.IsNotNull(actual); Assert.IsInstanceOfType(actual, expected); }
心得
使用 JustMock 的確達成我理想中完全不需調整測試目標程式的概念,但要不要實際採用 JustMock,我個人倒是持保留態度,原因詳述如下:
速度慢
執行測試時明顯感受到執行速度比未使用 JustMock 的版本緩慢,我自己測試下來常常超過 500 ms,而且偵錯時會慢更多,這會讓整個開發效率降低而不利測試程式的開發
價格不斐
如果是只購買 JustMock 一個人是 399 美金,我沒看到時間限制應該是買斷的,如果有助於整體開發這錢絕點值得花,但軟體的導入需要配合其他團隊成員及 CI server,這樣一來就不是想用自己買就可以解決的了
排除上述原因,JustMock 真的非常方便,尤其在面對 legacy code 時,讓開發人員可以專心在測試程式的撰寫上,而不需分心想著該如何重構才能讓程式具備可測試性,對於測試程式的開發實在是一大利器
參考資訊
文章作者 Yowko Tsai
上次更新 2021-11-02
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。