什麼是Mocking framework?它有什麼用?

來源:互聯網
上載者:User

原位地址:http://codetunnel.com/blog/post/what-is-a-mocking-framework-why-is-it-useful 

   今天我想講下關於mocking frameworks,並且解釋下他為什麼有用處。我將給你們展示用和不用mocking framework兩種測試方法。

假設我們已經有了一個Driver類:

public class Driver{    private IVehicle vehicleToDrive;    public Driver(IVehicle vehicleToDrive)    {        this.vehicleToDrive = vehicleToDrive;    }    public bool EvasiveManeuvers(bool alertOffendingDriver)    {        bool success = false;        if (alertOffendingDriver)            success = this.vehicleToDrive.ApplyBrakes() && this.vehicleToDrive.HonkHorn();        else            success = this.vehicleToDrive.ApplyBrakes();        return success;    }}

【注意】Driver類的建構函式依賴IVehicle介面!定義如下:

public interface IVehicle{    ///<summary>    ///Honks the vehicle's horn.    ///</summary>    ///<returns>    ///True if the action was successful.    ///</returns>    bool HonkHorn();    ///<summary>    ///Applies the vehicle's brakes.   ///</summary>    ///<returns>    ///True if the action was successful.    ///</returns>    bool ApplyBrakes();}

  現在我們需要寫2個單元測試來測試Driver。然而在我們寫之前,必須能夠通過Driver的建構函式才行,他依賴IVehicle介面。
很多人認為只要隨便實現IVehicle就可以了,這當然沒問題。事實上,如果你完成介面IVechicle,你也不用寫單元測試了。
單元測試是孤立性的,測試Driver類並且還要實現一個完整IVehicle實現不是單元測試,那是整體封閉測試。如果你在測試時出錯了,你不能確認是Driver出錯了還是其他的類。

我們現在搞一個假的IVehicle的實現來解決這個依賴問題。

public class FakeVehicle : IVehicle{    public int CalledHonkHorn = 0;    public int CalledApplyBrakes = 0;    public bool HonkHorn()    {        this.CalledHonkHorn++;        return true;    }    public bool ApplyBrakes()    {        this.CalledApplyBrakes++;        return true;    }}

注意我們定義的兩個int成員,在我們的單元測試中我們將用他們判斷HonkHorn()和ApplyBrakes()的調用情況。

現在我們可以寫單元測試了,我們將測試兩個行為:
1、Can_Evade_Trouble
2、Can_Evade_Trouble_And_Alert_Offending_Driver

[TestMethod]public void Can_Evade_Trouble(){    // Arrange (set up a scenario)    FakeVehicle fakeVehicle = new FakeVehicle();    Driver target = new Driver(fakeVehicle);    // Act (attempt the operation)    bool success = target.EvasiveManeuvers(false);    // Assert (verify the result)    Assert.IsTrue(success);    Assert.IsTrue(fakeVehicle.CalledHonkHorn == 0);    Assert.IsTrue(fakeVehicle.CalledApplyBrakes == 1);}[TestMethod]public void Can_Evade_Trouble_And_Alert_Offending_Driver(){    // Arrange (set up a scenario)    FakeVehicle fakeVehicle = new FakeVehicle();    Driver target = new Driver(fakeVehicle);    // Act (attempt the operation)    bool success = target.EvasiveManeuvers(true);    // Assert (verify the result)    Assert.IsTrue(success);    Assert.IsTrue(fakeVehicle.CalledHonkHorn == 1);    Assert.IsTrue(fakeVehicle.CalledApplyBrakes == 1);}

OK,現在我們成功的通過單元測試。他們的EvasiveManeuvers()方法都返回true,並且IVechicle.ApplyBrakes()方法每次都被調用了,HonkHorn()方法第一個測試沒有被調用,第二次調用了。
注意,在真正的測試驅動開發
TDD(Test Driven Development)我們首先要寫測試,然後才是寫代碼再測試,但我們沒這麼做。

這是一個比較不太討人喜歡的寫測試的風格,為了通過Driver我們寫了一個FakeVehicle,假如需要寫很多這種類那就麻煩了。

為瞭解決這種問題,Mocking Framework誕生了。

我們現在用一個叫Moq的架構(https://code.google.com/p/moq/),他的文法獨特,但用過之後你會感到寫起來很流暢。只要把moq.dll添加到引用,然後using Moq就可以了。

現在我們重寫上面的測試代碼,回頭再解釋它的牛逼的地方。

[TestMethod]public void Can_Evade_Trouble(){    // Arrange (set up a scenario)    Mock<IVehicle> mock = new Mock<IVehicle>();    mock.Setup(x => x.ApplyBrakes()).Returns(true);    Driver target = new Driver(mock.Object);    // Act (attempt the operation)    bool success = target.EvasiveManeuvers(false);    // Assert (verify the result)    Assert.IsTrue(success);    mock.Verify(x => x.HonkHorn(), Times.Never());    mock.Verify(x => x.ApplyBrakes(), Times.Once());}[TestMethod]public void Can_Evade_Trouble_And_Alert_Offending_Driver(){    // Arrange (set up a scenario)    Mock<IVehicle> mock = new Mock<IVehicle>();    mock.Setup(x => x.HonkHorn()).Returns(true);    mock.Setup(x => x.ApplyBrakes()).Returns(true);    Driver target = new Driver(mock.Object);    // Act (attempt the operation)    bool success = target.EvasiveManeuvers(true);    // Assert (verify the result)    Assert.IsTrue(success);    mock.Verify(x => x.HonkHorn(), Times.Once());    mock.Verify(x => x.ApplyBrakes(), Times.Once());}

不管你信不信,反正我們可以丟掉FakeVehicle類了。
Moq動態構造了介面的實作類別,所有的成員預設的值都是其預設值。
由於bool類型的預設值是false,所以HonkHorn()ApplyBrakes()在mock.Object執行個體中都將返回false,顯然我希望返回true的,所以用Moq的Setup()方法來解決。

Setup參數是一個lambda運算式,可以強型別的方式直接存取到其成員。例如

mock.Setup(x=>x.HonkHorn().Returns(true));

如果不用lambda用字串,類似mock.Setup("HonkHorn").Returns(true),這種方式比較醜,如果介面變化了這邊就該報錯了。
moq用lambda就是保證所有的訪問都是強型別的。

另外如果你的方法接受一些參數比如string例如

mock.Setup(x => x.HonkHorn("loudly");

如果這個值不是必須的(不是這個值就不能通過),那就可以用It類代替,他包含很多有用的方法。下面的例子就是接受任一字元串

mock.Setup(x => x.HonkHorn(It.IsAny<string>())).Returns(true);

Moq可以讓你建立任意類型T的Mock<T>執行個體,然後調用Setup()去設定屬性或方法的傳回值,隨便什麼值只要是為你達到測試的目的。加入你需要一個屬性返回一個集合,你只需要定義好集合類,並且通過Setup()的Returns方法返回集合就行了。當這個mock.Object的集合屬性被訪問時就會返回你定義好的集合。
記住:1、mock的對象不是你的測試,你mock的對象是讓你能夠通過他們進入你要測試的類/組件。
另外注意 var mock = new Mock<T>。mock可不是T的執行個體,mock是Mock<T>的對象執行個體,T的執行個體是mock.Object。所以不要搞混了;執行個體化Driver時要用mock.Object。

//Do thisMock<IVehicle> mock = new Mock<IVehicle>();Driver driver = new Driver(mock.Object);//Not thisMock<IVehicle> mock = new Mock<IVehicle>();Driver driver = new Driver(mock);

現在回過頭看看我們的moq的單元測試,所有的結果都是對的。調用次數也是對的,moq能自動記錄方法的調用次數,我們只需要調用mock.Verify()然後通過lambda運算式就能驗證我們想要的次數是否正確。

這裡是最基礎的moq用法,希望你現在能夠明白mockingframework的用處並明白moq怎麼完成工作的。

  

  

  

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.