黃偉榮的學習筆記

軟體的世界變化萬千,小小的我只能在這洪流奮發向上以求立足。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Moles - Isolation framework for .NET(假.Net)介紹

Posted on 2010-11-26 00:37  黃偉榮  阅读(3679)  评论(2编辑  收藏  举报

Moles是前陣子91大推薦我用的Mock的Framework,它跟Moq與Rhino.Mocks這類的套件不同,Moq與Rhino.Mocks這類的Mock是對Interface或Abstract Class做Mock,而Moles是Mock整個CLR(Common Language Runtime),破除Moq等Mock套件的限制,非Interface或Abstract Class也可以Mock,而且還可以對Static Method來Mock,如DateTime.Now或File.Open等等Static Method,使原本在開發時不用為了方便測試而過度設計(如小弟之前為了方便測試,把DateTime又加了一層DateTimeProvider,當我看到這Framework後,就覺得我之前的作法非常的無聊,可參考Unit Test小技巧 : DateTime的Stub)。

 

•什麼是Mock

Mock簡單來說就是作一個假的東西,通常用在單元測試,因為寫單元測試時,難免沒辦法抽的很乾淨,多少會呼叫到別的Method,如寫訂單的處理,其中有呼叫到信用卡API,在寫單元測試不需要也盡可能不要呼叫到信用卡API,所以用Mock把信用卡API變成假的信用卡API,讓訂單的測試可以獨立,Mock的用法與概念當然不只這樣,詳情可以參考:

NOTE:

Mock與Stub都是用來作假物件,只是二個有實作上的差異,簡單來說,Mock是用動態產生的(如用Moq產生),Stub是已經寫好的(如為了測試實作一個Interface),本文統一都叫作Mock,雖然Moles感覺比較像Stub。

 

•Moq與Moles比較

Moq的Mock原理是動態產生Class,所以它才只能對Interface或Abstract Class,這類可繼承的型別,如對Interface就是動態產生一個實作Interface所有成員的Class,在用Setup來指定成員的內容或回傳值,多半會搭配IoC,讓程式在呼叫其他類別時,改為呼叫Mock的程式。

NOTE:

大部分Mock的Framework如Moq與Rhino Mocks的底層都是使用Castle DynamicProxy,來動態產生Class。

 

Moles的Mock原理是,在CLR上在建立一個自己的Runtime,測試時是在MoleRuntime上執行,所以原本是直接呼叫如DateTime.Now,改為呼叫Mock的程式。

image

圖一 MoleRuntime的假想圖

 

Moq與Moles都有適合的使用時機與限制,可參考圖二。

image

圖二 使用Mole與一般Mock的差異 來源:Microsoft Moles Reference Manual

 

•安裝Moles

Moles

Visual Studio 2010 Moles - Isolation Framework for .NET(x86)

Visual Studio 2010 Moles - Isolation Framework for .NET(x64)

 

Pex And Moles

Pex - Automated Whitebox Testing for .NET(x86)

x64版目前只在MSDN Suscribers中下載

NOTE:

Pex是一個Code的分析工具,可以產生相關的單元測試,都是同一個團隊開發,他們把二套Framework綁在一起,個人試用的結果,很有趣,但實用性不高。

 

•實作DateTime.Now的Mock

1.新增測試專案

 

2.在測試專案中的參考上按右鍵,選Add Moles Assembly for mscorlib,產生mscorlib.moles檔

image

圖三 新增mscorlib.moles檔

 

當專案中有.moles檔時,編譯會產生其對應的組件

image

圖四 mscorlib.moles檔與其對應的組件

NOTE:

在舊版的Moles中,mscorlib.moles檔的新增是在新增項目時自行輸入組件名稱,新版直接在參考中右鍵新增*.moles,方便很多。

image

圖五 新增.moles已取消從新增項目中新增的方式,因為找不到圖了借用程湘大的圖片一用。

 

mscorlib是.Net Framework是最基本的函式庫,俗稱Base Class Library(BCL),也就是把專案中的所有參考都刪除,剩下可以用的型別就是在mscorlib.dll中的型別,DateTime也是其一,而所有不同類型專案的mscorlib.dll一定都相同,如WebApplicationSilverlightmscorlib.dll是一致的(這小弟我沒記錯是這樣,如果說錯了請見諒)。

 

如果想要Mock其他組件在其他組件上按右鍵新增該組件的.moles檔。

image

圖六 新增其他Moles組件

 

3.於TestMethod中撰寫DateTime的測試,且Mock DateTime並驗證。

[TestMethod]
[HostType("Moles")]
public void TestMethod1()
{
    System.Moles.MDateTime.NowGet = () => { return new DateTime(2010, 1, 1); };

    Assert.AreEqual(new DateTime(2010, 1, 1), DateTime.Now);
}

 

在TestMethod上增加[HostType("Moles")],讓Visual Studio知道這一個TestMethod,是要用MoleRuntime執行,Mock的方式是用Delegate,如圖四Moles會建立mscorlib的Mock用組件,其邏輯如下:

  • Namespace:最後加上Moles
  • Type:在前加上M
  • Property:後方加上Get或Set
  • Method:後方加上參數的型別
  • Instance:在AllInstances下操作

所以System.DateTime.Now的Mock在System.Moles.MDateTime.NowGet,範例中DateTime.Now就改為Mock的值,而不是現在時間。

 

4.增加MoledType宣告。

[assembly: Microsoft.Moles.Framework.MoledType(typeof(DateTime))]

在檔案的最上方或AssemblyInfo.cs中加上MoledType的宣告,每一個要Mock的Type都要增加。

 

5.執行測試,並通過測試。

 

•已知問題

我在使用Moles版本0.94.51023.0時,有遇到一個問題,就是MoleRuntime無法正確取得測試專案的app.config,我也有在MSDN中詢問別人,得知的訊息是Bug,可參考Can't get data via ConfigurationManager.AppSettings on HostType("Moles"),但山不轉路轉,聰明的方法沒有,笨死人的解決方法到有,MoleRuntime只會抓自己的app.config,所以只要將資料寫在MoleRuntime的app.config中測試時就可以正常讀取資料,只是設定就沒辦法很方便的xCopy,且會所有專案共用一個app.config,最後我只想到切換專案時,手動更換檔案。

image

圖七 各測試專案需要的app.config

Moles的執行檔案路徑在

C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.Moles.VsHost.x86.exe

 

•其他範例

//測試時只用一組固定的Guid
System.Moles.MGuid.NewGuid = () =>
{
    return new Guid("FFEB1621-5016-4F28-94AE-5DB72FC8A23B");
};           

//測試時不需要真的讀實體檔案
System.IO.Moles.MFile.ReadAllTextString = (filePath) =>
{
    return "測試內容";
};

System.Net.Mail.Moles.MSmtpClient.AllInstances.SendMailMessage = (smtp, message) =>
{
    //測試時不真的寄Email
};

 

•參考資料

Moles - Isolation framework for .NET

程湘之間 - 單元測試(7): 使用 Moles 增加可測試性

Moq

Rhino.Mocks