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();
    }
  }
}

 

 

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 py3939 的頭像
    py3939

    老爹寫程式

    py3939 發表在 痞客邦 留言(0) 人氣()