NMock 詳細的說明可以上官方網站詳讀. 簡單的說NMock 是一個 .NET 的動態 mock 物件, 當我們的測試標的與其他 class 有關聯時, 提供一種模擬技術, 方便於我們可以單獨測試此一標的 class.
舉一個學生成績系統為例 : 有兩個人正在進行此系統開發.
Jack 負責 TotalSummary class, 此 class 有一個 Sum method, 會傳回所指定的同學 ID 所有科目的成績總和.
Alan 負責主程式, 進行到計算出第一名的同學是誰 GetTop1 method, GetTop1 實做的方式是依據每位同學的 ID 當作輸入值, 呼叫 TotalSummary.Sum() 取的總分來判斷出第一名的同學.
問題來了, 因為兩同時開發, 那 Alan 開發的 GetTop1 method 會使用 Jack 開發的 TotalSummary.Sum method, 是不是要先等 Jack 開發好呢? 以往我們可能會先定好 Interface, 然後 Hard code 一些傳回值, 好讓 Alan 可以先開發, 不要因此等待, 等 TotalSummary.Sum method 開發好後, 再來整合測試. 這樣進行的方式有個問題, 就是測試的 Scope 被限制住了. 然而 NMock 就是用來模擬 TotalSummary.Sum method , 讓我們可以隨心所欲的模擬各種情境, 讓我們把重點放在 GetTop1 method.
要模擬的 method 都要繼承自 Interface, 屆時 NMock 就是藉此運作.
using System;
using System.Collections.Generic;
using System.Text;
namespace NMockExample
{
public interface ITotalSummary
{
int Sum(string StudentID);
}
public class TotalSummary : ITotalSummary
{
public int Sum(string StudentID)
{
return 600;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace NMockExample
{
public class StudentScore
{
private ITotalSummary _TotalSummary;
public ITotalSummary TotalSummary
{
get
{
if (this._TotalSummary == null)
{
this._TotalSummary = new TotalSummary();
}
return this._TotalSummary;
}
set
{
this._TotalSummary = value;
}
}
/// <summary>
/// 取得第一高分的同學 ID
/// </summary>
/// <param name="IDList"></param>
/// <returns></returns>
public string GetTop1(List<string> IDList)
{
int MaxValue=0;
string Top1="";
foreach (string ID in IDList)
{
int Value = this.TotalSummary.Sum(ID); //當我使用 NMock 做測試時, Sum() 不會被呼叫, 而是依據我們的指定傳回結果.
if ( Value > MaxValue)
{
Top1 = ID;
MaxValue = Value;
}
}
return Top1;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using NMock2;
using NUnit.Framework;
using NMockExample;
namespace NMockExample
{
[TestFixture]
public class StudentScoreTest
{
StudentScore _StudentScore;
Mockery _mocks;
ITotalSummary _TotalSummary;
[SetUp]
public void Setup()
{
this._mocks = new Mockery();
this._TotalSummary = _mocks.NewMock<ITotalSummary>();
this._StudentScore = new StudentScore();
this._StudentScore.TotalSummary = this._TotalSummary;
}
[Test]
public void Sum_Mock_Test()
{
Expect.Once.On(this._TotalSummary).Method("Sum").With("A01").Will(Return.Value(599));//設定 Sum method 會被呼叫一次, 且傳入 "A01", 傳回結果 699.
Assert.AreEqual(599, this._StudentScore.TotalSummary.Sum("A01"));//當執行時, Sum method 實際是沒有被呼叫的, 而是由 NMock 模擬傳回結果 599.
this._mocks.VerifyAllExpectationsHaveBeenMet();//檢查所有的預期是像是發都發生. 即 Sum method 被呼叫一次.
}
[Test]
public void GetTop1_Test()
{//這一個例子, GetTop1 method 內呼叫 Sum method 使用以下輸入, 輸出模擬.
Expect.Once.On(this._TotalSummary).Method("Sum").With("A01").Will(Return.Value(597));
Expect.Once.On(this._TotalSummary).Method("Sum").With("A02").Will(Return.Value(599));
Expect.Once.On(this._TotalSummary).Method("Sum").With("A03").Will(Return.Value(598));
Assert.AreEqual("A02", this._StudentScore.GetTop1(new List<string>(new string[] {"A01","A02","A03" })));
this._mocks.VerifyAllExpectationsHaveBeenMet();
}
}
}