[工具] 單元測試(Unit Test) - NMock

[ASP.NET]單元測試工具-NMock

摘要

本篇主要著重在ASP.NET的單元測試工具NMock上,

在使用NMock時心中燃起了一個疑問,

.NET平台已經有提供像是NUnit這種自動化單元測試的framwork,

為什麼還要用NMock呢?

NMock還是有其獨特的優點,

當我們的待測程式有透過介面(Interface)呼叫其他外部程式時,

則可使用NMock來模擬該介面,以降低程式間的耦合性。

以下的應用實例將說明如何善用NMock工具來協助我們進行單元測試。

先附上NMock2的下載網址:http://sourceforge.net/projects/nmock2/

 

step1:先建立一個主控台應用程式專案,並定義一個簡單的Interface。

 

 

 

image

 

step3:針對Hello.Greet() 這個method進行單元測試。

對我們來說,IPerson.GetName()這個Interface並不是我們在進行Hello.Greet()單元測試所關注的。

故我們模擬(mock) IPerson這個Interface ,並用hardcode的方法給定GetName()的值。

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5:  
   6: namespace NMock
   7: {
   8:  
   9:     class Hello
  10:     {
  11:         static void Main(string[] args)
  12:         {
  13:         }
  14:  
  15:         IPerson person;  //--This method invokes the interface method.
  16:  
  17:         public Hello(IPerson person)
  18:         {
  19:             this.person = person;
  20:         }
  21:         public String Greet()
  22:         {
  23:             return "Hello " + person.GetName();
  24:         }
  25:  
  26:     }
  27: }

 

step4:建立單元測試。

image

image

 

step5:在測試專案中,它會自動產生程式碼。

我們將他改成NMock程式碼。

 

   1: /// <summary>
   2: ///Greet 的測試
   3: ///</summary>
   4: [TestMethod()]
   5: public void GreetTest()
   6: {
   7:     //1. 建立一個mock object
   8:     Mockery personMock = new Mockery();
   9:     //2. 用mock object來模擬IPerson這個Interface
  10:     IPerson p = personMock.NewMock<IPerson>();
  11:     //3. 給定模擬結果一個值, ex:GetName()給他returnValue = “Neil"
  12:     string returnValue = "Neil";
  13:     //4. 模擬呼叫IPerson.GetName() 這個method的結果
  14:     Expect.Once.On(p).Method("GetName").WithNoArguments().Will(Return.Value(returnValue));
  15:     //5. 建立target  class object 
  16:     Hello h = new Hello(p);
  17:     //6. 呼叫target  test method 
  18:     Assert.AreEqual("Hello Neil", h.Greet());
  19: }

 

step6:測試->執行->可選目前內容中的測試或方案中所有測試。

image

image

 

step7:我們試著開啟另一個測試專案,並將其預設結果設為("Hello Pou")。

 

 

   1: /// <summary>
   2: ///Greet 的測試
   3: ///</summary>
   4: [TestMethod()]
   5: public void GreetTest()
   6: {
   7:     Mockery personMock = new Mockery();
   8:     IPerson p = personMock.NewMock<IPerson>();
   9:     string returnValue = "Neil";
  10:     Expect.Once.On(p).Method("GetName").WithNoArguments().Will(Return.Value(returnValue));
  11:     Hello h = new Hello(p);
  12:     Assert.AreEqual("Hello Neil", h.Greet());
  13: }
  14:  
  15: /// <summary>
  16: ///Greet 的測試
  17: ///</summary>
  18: [TestMethod()]
  19: public void GreetTest1()
  20: {
  21:     Mockery personMock = new Mockery();
  22:     IPerson p = personMock.NewMock<IPerson>();
  23:     string returnValue = "Neil";
  24:     Expect.Once.On(p).Method("GetName").WithNoArguments().Will(Return.Value(returnValue));
  25:     Hello h = new Hello(p);
  26:     Assert.AreEqual("Hello Pou", h.Greet());
  27: }

 

step8:可以看到第二個測試專案失敗。

因為returnValue = "Neil",故測試的預其結果應為("Hello Neil"),

與我們預設的結果("Hello Pou")不同,故產生錯誤訊息。

image

 

小結:

Mock 在每個測試中都必須明確定義所有例外狀況,

在此應用實例只是簡單的小程式,

當我們專案中所要測試的目標與Mock Object之間的互動越密切,

我們就要定義更多的期望值,這將有可能使我們的測試變得耗工又費時。