如題,本文主要做爲在VS2012使用Fakes的入門示例,開發工具必須是VS2012或更高版本。html
關於Fakes的MSDN地址:http://msdn.microsoft.com/en-us/library/hh549175.aspx框架
關於VS2012單元測試的前期文章:工具
1.《在Visual Studio 2012使用單元測試》、post
2.《VS2012 單元測試之泛型類(Generics Unit Test)》、單元測試
3.《VS2012 Unit Test —— 我對接口進行單元測試使用的技巧》開發工具
4.《VS2012 Unit Test(Void, Action, Func) —— 對無返回值、使用Action或Func做爲參數、多重載的方法進行單元測試》測試
依我我的理解單元測試就是對程序的小單元進行測試,一個測試不該包含兩個或更多單元,整體而言大多都是對方法、屬性的編碼正確性進行驗證。可是每每一個方法又會調用其餘的方法或屬性,我這裏暫稱之爲外部依賴,於是外部依賴會影響程序單元的測試結果,要避免這樣的狀況就不得不使用一些外部依賴的模擬進行隔離(Isolate),本文就是使用了Microsoft Fakes,固然還有其餘更爲流行的框架能夠選擇使用(Moq、Rhino Mocks、Type Mock)this
Fakes有兩種形式:stub 和 shim。具體的介紹我就不囉嗦,由於我英文很差可能會表達錯誤誤導新人。編碼
個人Demo也是看了MSDN後以我的理解後進行簡單的編寫,若是MSDN看懂了也就不用看如下內容了,期待和我同樣正在使用VS2012 MSTest進行單元測試的一塊兒交流進步。url
如下將模擬DateTime的Now屬性,假設我如今須要在活動服務類ActivityService添加一個方法驗證某個線下活動是否過時。
1. 打開VS2012,建立單元測試項目FakesTesting,我這是測試先行。重命名項目自動生成的類UnitTest1爲ActivityServiceTest,將TestMethod1改成IsExpireTest(是否過時).
2. 添加代碼「ActivityService service = new ActivityService();」並使用VS快捷功能爲咱們建立ActivityService 類
3. 添加Fakes,因爲DateTime位於System程序集,於是將添加System的Fake程序集(右鍵System程序集), 而後在測試類「using System.Fakes;」
4. 編寫測試代碼以下
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Fakes; using Microsoft.QualityTools.Testing.Fakes; namespace FakesTesting.Test { [TestClass] public class ActivityServiceTest { [TestMethod] public void IsExpireTest() { ActivityService service = new ActivityService(); bool actual = service.IsExpire(); Assert.IsFalse(actual); using (ShimsContext.Create()) { ShimDateTime.NowGet = () => new DateTime(2014, 5, 5); actual = service.IsExpire(); Assert.IsFalse(actual); } } } }
5. 而後編寫ActivityService類
public class ActivityService { public DateTime BeginTime { get; set; } public ActivityService() { this.BeginTime = new DateTime(2014, 3, 3); //僅做演示,無心義 } public bool IsExpire() { return BeginTime >= DateTime.Now; } }
6. 運行測試經過。而後就能夠把實際業務類移動到相應VS項目中,並調整命名空間。
如今假設ActivityService類有一個方法獲取是否還能報名,可是它依賴於倉儲IActivityRepository(只有遵循依賴反轉與接口隔離原則的代碼纔好使用Stub填充外部依賴)提供的RegisterNumber方法。
1. IActivityRepository接口(新建IRepositories項目並添加該接口)
public interface IActivityRepository { /// <summary> /// 已報名人數 /// </summary> int RegisterNumber(); }
2. 而咱們的單元測試如今不能依賴具體(實際環境中的Repository可能對測試帶來影響),這時候就能使用Stub來填充該接口了,添加IRepositories引用,而後與上一個Demo同樣的添加IRepositories的Fakes程序集。
3. 在測試類中添加Using代碼
using IRepositories; using IRepositories.Fakes;
4. 編寫測試代碼
[TestMethod] public void CanRegisterTest() { StubIActivityRepository repository = new StubIActivityRepository(); ActivityService service = new ActivityService(repository); //若是已報名人數小於最多可報名數量則不能再報名,斷言CanRegister方法應爲True repository.RegisterNumber = ()=> 20; bool actual = service.CanRegister(); Assert.IsTrue(actual); //若是已報名人數大於等於最多可報名數量則不能再報名,斷言CanRegister方法應爲False repository.RegisterNumber = () => 50; actual = service.CanRegister();
Assert.IsFalse(actual);
}
5. ActivityService代碼:
public class ActivityService { public DateTime BeginTime { get; set; } /// <summary> /// 最多可報名數量 /// </summary> private int maxCount = 50; private IActivityRepository repository; public ActivityService() { this.BeginTime = new DateTime(2014, 3, 3); //僅做演示,無心義 } public ActivityService(IActivityRepository repository) { // TODO: Complete member initialization this.repository = repository; } public bool IsExpire() { return BeginTime >= DateTime.Now; } public bool CanRegister() { return repository.RegisterNumber() < this.maxCount; } }
stub用於咱們可控的代碼,shim用於不可控的,例如.NET Framework以及第三方類庫等。