Moq 一個用來模擬物件的類別庫

Jun 3, 2014

4 mins read

前言

當系統利用介面完成物件來隔離外部物件之後,為了方便測試通常會寫一些假的物件來抽換 但功能越來越多之後,測試的假類別也越多,檔案不好管理 這時後可以讓Moq利用反射的方式讓我們很容易地新增假物件 Moq只能模擬公開的介面,如果是繼承的類別則需要Virtual才能模擬

要使用Moq很簡單,利用NuGet安裝套件就行了

安裝moq](http://1.bp.blogspot.com/-cwTzcXKv6l0/U408MjPlOYI/AAAAAAAABXU/yxksMr1XfO8/s1600/01.Moq.png)

用一個簡單的IFoo介面來當例子

public interface IFoo
{
    void F1();
    int F2();
    int F3(int x);
    event EventHandler<object> MyEvent;
}

首先建立一個IFoo型別的Mock物件 在建構式中可以指定MockBehavior來設定模擬物件的程度 Strict是限制需要完整模擬物件,Loose則不需要 預設為Default,也就是Loose

private Mock<IFoo> mock;

[TestInitialize]
public void MyTestInitialize()
{
    this.mock = new Mock<IFoo>();
}

一開始先用沒有回傳值的函式當例子,就先不做更多的設定 直接使用Mock物件的Object屬性取得模擬的假物件就可以使用了 這裡用Verify和Times來檢查該函式的呼叫次數

[TestMethod]
public void TestMethod1()
{
    Mock<IFoo> foo = new Mock<IFoo>();
    foo.Object.F1();
    foo.Verify(x => x.F1(), Times.Once());
}

再來用一個有回傳值的函式當例子,所以再加上回傳值的設定 這裡的意思是呼叫F2函式的時後,總是回傳123的值回來

[TestMethod]
public void TestMethod2()
{
    Mock<IFoo> foo = new Mock<IFoo>();
    foo.Setup(x => x.F2()).Returns(123);
    Assert.AreEqual(123, foo.Object.F2());
}

函式中的參數可以透過It物件來設定 這裡的意思是只要傳入int型別的參數,就回傳456的值

[TestMethod]
public void TestMethod3()
{
    Mock<IFoo> foo = new Mock<IFoo>();
    foo.Setup(x => x.F3(It.IsAny<int>())).Returns(456);
    Assert.AreEqual(456, foo.Object.F3(1));
}

除了回傳值之外,也可以設定Callback函式

[TestMethod]
public void TestMethod4()
{
    int counter = 0;
    Mock<IFoo> foo = new Mock<IFoo>();
    foo.Setup(x => x.F1()).Callback(() => counter++);
    foo.Object.F1();
    Assert.AreEqual(1, counter);
}

還有丟回指定的Exception

[TestMethod]
[ExpectedException(typeof(Exception))]
public void TestMethod5()
{
    Mock<IFoo> foo = new Mock<IFoo>();
    foo.Setup(x => x.F1()).Throws(new Exception());
    foo.Object.F1();
}

還有引發指定的事件

[TestMethod]
public void TestMethod6()
{
    Mock<IFoo> foo = new Mock<IFoo>();
    foo.Raise(x => x.MyEvent += (s, a) => { Console.WriteLine(a); }, 123);
}

參考資料 Moq Quickstart

Sharing is caring!