2011-04-19 55 views
2

下面是我在我的測試,到目前爲止:如何在單元測試中模擬字符串響應?

[TestFixture] 
public class IndividualMovieTests 
{ 
    [Test] 
    public void WebClient_Should_Download_From_Correct_Endpoint() 
    { 
     const string correctEndpoint = "http://api.rottentomatoes.com/api/public/v1.0/movies/{movie-id}.json?apikey={your-api-key}"; 
     ApiEndpoints.Endpoints["IndividualMovie"].ShouldEqual(correctEndpoint); 
    } 

    [Test] 
    public void Movie_Information_Is_Loaded_Correctly() 
    { 
     Tomato tomato = new Tomato("t4qpkcsek5h6vgbsy8k4etxdd"); 
     var movie = tomato.FindMovieById(9818); 
     movie.Title.ShouldEqual("Gone With The Wind"); 
    } 
} 

我FIndMovieById方法上線並獲取一個JSON的結果,這意味着它像是打破原則背後單元測試。我有一種感覺,我必須嘲笑這個字符串響應,但我不知道如何處理這個問題。

你會怎麼做這個特定的單元測試?

+2

您是否考慮過使用模擬框架?如果您要爲進行外部API調用的方法進行大量的單元測試,這可能會非常有用。 – 2011-04-19 03:34:27

回答

3

在您的第二個[Test]中,除非您真的想要測試您的給定輸入應始終導致「飄」,否則我建議不要關注來自您的FindMovieById方法的特定返回值。你所擁有的測試似乎是一個非常具體的測試案例,其中一個特定的輸入數字會導致一個特定的輸出,這個輸出在你的實際數據庫上運行時可能會也可能不會改變。另外,由於您不會對實際的Web服務進行測試,因此進行這種驗證基本上是自我服務的 - 您並未真正測試任何內容。相反,請着重測試Tomato類如何處理參數驗證(如果有的話),並且Tomato類實際調用服務以獲取返回值。測試具體的輸入和輸出,而不是測試具體的輸入和輸出,以便測試該類的行爲,以便將來如果有人改變它,測試應該中斷以提醒他們他們可能已經破壞了工作功能。

例如,如果您有輸入驗證,則可以測試您的Tomato類是否檢測到無效輸入時引發異常。

假設您的Tomato類具有用於請求和檢索結果的某種Web客戶端功能,您可以插入一些實際Web代碼的存根實現,或者模擬實現來確保Tomato實際上調用適當的Web客戶端代碼來請求和處理響應。

2

首先,你可能不需要模擬測試你的代碼。例如,如果您只是測試您可以將JSON反序列化爲Movie對象,那麼可以通過在Movie類上測試公開或內部ParseJSON秒來執行此操作。

但是,既然您在問關於嘲笑,下面是您可以使用模擬來編寫此測試的一種方法的快速概述。正如它所寫,Movie_Information_Is_Loaded_Correctly()看起來像一個集成測試。爲了把它變成一個單元測試,你可以模擬出Tomato類所做的web請求。一種方法是創建一個ITomatoWebRequester接口,並將其作爲參數傳遞給構造函數中的Tomato類。然後,您可以模擬ITomatoWebRequester以返回您期望的網絡響應,然後您可以測試Tomato類正確分析該響應。

的代碼可能是這個樣子:

public class Tomato 
{ 
    private readonly ITomatoWebRequester _webRequester; 
    public Tomato(string uniqueID, ITomatoWebRequester webRequester) 
    { 
     _webRequester = webRequester; 
    } 

    public Movie FindMovieById(int movieID) 
    { 
     var responseJSON = _webRequester.GetMovieJSONByID(movieID); 
     //The next line is what we want to unit test 
     return Movie.Parse(responseJSON); 
    } 
} 

public interface ITomatoWebRequester 
{ 
    string GetMovieJSONByID(int movieID); 
} 

爲了測試,你可以使用像起訂量嘲弄的框架來創建一個ITomatoWebRequester將返回你所期望的結果。要做到這一點與起訂量下面的代碼應該工作:

[Test] 
public void Movie_Information_Is_Loaded_Correctly() 
{ 
    var mockWebRequester = new Moq.Mock<ITomatoWebRequester>(); 
    var myJson = "enter json response you want to use to test with here"; 
    mockWebRequester.Setup(a => a.GetMovieJSONByID(It.IsAny<int>()) 
     .Returns(myJson); 

    Tomato tomato = new Tomato("t4qpkcsek5h6vgbsy8k4etxdd", 
     mockWebRequester.Object); 
    var movie = tomato.FindMovieById(9818); 
    movie.Title.ShouldEqual("Gone With The Wind"); 
} 

約在這種情況下,模擬很酷的事情是,你不必擔心所有的箍實際ITomatoWebRequester具有通過跳躍來返回JSON它應該返回,你可以在你的測試中創建一個模擬權,它可以返回你想要的結果。希望這個答案可以作爲嘲笑的體面介紹。我肯定會建議閱讀嘲笑的框架,以更好地感受過程如何工作。

1

使用Rhino.Mocks庫並在任何適當的地方調用Expectations。以下是嘲笑你的電影對象的示例。

using System; 
using NUnit.Framework; 
using Rhino.Mocks; 
namespace ConsoleApplication1 
{ 
    public class Tomato 
    { 
     public Tomato(string t4qpkcsek5h6vgbsy8k4etxdd) 
     { 
      // 
     } 

     public virtual Movie FindMovieById(int i) 
     { 
      return null; 
     } 
    } 

    public class Movie 
    { 
     public string Title; 

     public Movie() 
     { 

     } 

     public void FindMovieById(int i) 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    [TestFixture] 
    public class IndividualMovieTests 
    { 
     [Test] 
     public void Movie_Information_Is_Loaded_Correctly() 
     { 

      //Create Mock. 
      Tomato tomato = MockRepository.GenerateStub<Tomato>("t4qpkcsek5h6vgbsy8k4etxdd"); 

      //Put expectations. 
      tomato.Expect(t=>t.FindMovieById(0)).IgnoreArguments().Return(new Movie(){Title ="Gone With The Wind"}); 

      //Test logic. 
      Movie movie = tomato.FindMovieById(9818); 

      //Do Assertions. 
      Assert.AreEqual("Gone With The Wind", movie.Title); 

      //Verify expectations. 
      tomato.VerifyAllExpectations(); 
     } 
    } 
} 
相關問題