如題,本文主要作為在VS2012使用Fakes的入門示例,開發工具必須是VS2012或更高版本。
關于Fakes的MSDN位址:http://msdn.microsoft.com/en-us/library/hh549175.aspx
關于VS2012單元測試的前期文章:
1.《在Visual Studio 2012使用單元測試》、
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)
Fakes有兩種形式:stub 和 shim。具體的介紹我就不啰嗦,因為我英文不好可能會表達錯誤誤導新人。
我的Demo也是看了MSDN後以個人了解後進行簡單的編寫,如果MSDN看懂了也就不用看以下内容了,期待和我一樣正在使用VS2012 MSTest進行單元測試的一起交流進步。
一、shim
以下将模拟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項目中,并調整命名空間。
二、Stub
現在假設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以及第三方類庫等。
感謝閱讀,請留下您的意見或疑問! 能力有限,錯漏難免,歡迎指點!
分割線:我的個人原創,請認準 http://freedong.cnblogs.com/ (轉摘不标原文出處可恥)