2012-02-14 108 views
3

我有一點完美的風暴阻止了我測試課程。該課程是RestClient,它包裝了內部的HttpClient(我不能修改)。 HttpClient上的ExecuteMethod方法無效。它接受IHttpMethod,並根據來自服務器的響應修改此對象。我想嘲笑ExecuteMethod,以便它爲我修改IHttpMethod。我正在嘗試使用Callback來實現此目的,但它不起作用。使用Moq將參數修改爲虛擬無效方法

這裏的發送請求的代碼:

var httpClient = this.httpClientFactory.CreateHttpClient(); 
httpClient.ExecuteMethod(method); 

var response = this.GetResourceResponse<T>(method.ResponseBodyAsStream.AsString()); 
response.ResponseHeaders = method.ResponseHeaders; 
response.Status = method.StatusCode.ToString(); 
response.StatusCode = (int)method.StatusCode; 

return response; 

這是我的企圖在嘲諷:

var mockHttpMethod = new Mock<IHttpMethod>(); 
mockHttpMethod.Setup(m => m.ResponseBodyAsStream).Returns(new MemoryStream(Encoding.UTF8.GetBytes("foo"))); 

var modifyHttpMethod = new Action<IHttpMethod>(m => 
{ 
    m = mockHttpMethod.Object; 
}); 

var mockHttpClient = new Mock<IHttpClient>(); 
mockHttpClient.Setup(c => c.ExecuteMethod(It.IsAny<IHttpMethod>())) 
    .Callback<IHttpMethod>(modifyHttpMethod); 

var mockHttpClientFactory = new Mock<ILegalHoldHttpClientFactory>(); 
mockHttpClientFactory.Setup(f => f.CreateHttpClient()).Returns(mockHttpClient.Object); 

var restClient = new RestClient(mockHttpClientFactory.Object); 

當執行modifyHttpMethod行動,我看到兩件事情,這兩者我期待:

  1. 傳入IHttpMethodm)有p我期望它具有的性能。
  2. 將模擬對象分配給m後,它包含我在測試中設置的殘段值。

然而,在執行後回調,並控制返回到我的應用程序的代碼,我method變量仍然有我在上面的步驟1,嘗試讀取method.ResponseBodyAsStream時導致空引用異常看到了它的舊值。

我試圖做什麼甚至可以實現?如果是這樣,怎麼樣?謝謝!

回答

2

我複製你的設置相一相嘲諷,並不能找到它的任何問題:

public interface IHttpMethod 
{ 
    MemoryStream ResponseBodyAsStream { get; set; } 
} 

public interface IHttpClient 
{ 
    void ExecuteMethod(IHttpMethod method); 
} 

public class HttpClient : IHttpClient 
{ 

    #region IHttpClient Members 

    public void ExecuteMethod(IHttpMethod method) 
    { 

    } 

    #endregion 
} 

public class Factory 
{ 
    public virtual IHttpClient CreateHttpClient() 
    { 
     return new HttpClient(); 
    } 
} 

public class ClassUnderTest 
{ 
    private readonly Factory _factory; 

    public ClassUnderTest(Factory factory) 
    { 
     _factory = factory; 
    } 

    public string GetResponseAsString(IHttpMethod method) 
    { 
     var myClient = _factory.CreateHttpClient(); 
     myClient.ExecuteMethod(method); 

     return method.ResponseBodyAsStream.ToString(); 
    } 
} 

[TestClass] 
public class ScratchPadTest 
{ 
    [TestMethod] 
    public void SampleTest() 
    { 
     var mockHttpMethod = new Mock<IHttpMethod>(); 
     mockHttpMethod.Setup(x => x.ResponseBodyAsStream).Returns(new MemoryStream(Encoding.UTF8.GetBytes("foo"))); 

     var modifyHttpMethod = new Action<IHttpMethod>(m => 
     { 
      m = mockHttpMethod.Object; 
     }); 

     var mockHttpClient = new Mock<IHttpClient>(); 
     mockHttpClient.Setup(c => c.ExecuteMethod(It.IsAny<IHttpMethod>())).Callback<IHttpMethod>(modifyHttpMethod); 

     var myFactoryStub = new Mock<Factory>(); 
     myFactoryStub.Setup(f => f.CreateHttpClient()).Returns(mockHttpClient.Object); 

     var myCut = new ClassUnderTest(myFactoryStub.Object); 

     Assert.IsNotNull(myCut.GetResponseAsString(mockHttpMethod.Object)); 
    } 

} 

該測試通過,這意味着內存流不爲空(否則一個例外是生成)。我可以看到的唯一X因素是你的AsString()擴展方法(我假設這是一個擴展方法,因爲intellisense並沒有在MemoryStream中顯示給我)。你的問題能在那裏嗎?

而且,順便說一句,你試圖做的事情幾乎可以肯定是用Moq實現的。

+0

我不認爲問題是與擴展方法。我可以看到擴展方法如何讀取流並關閉它,使其不可用,但在AsString()方法執行之前流實際上不可用。我不知道爲什麼你的代碼可以工作,而我不知道。我最終做了一些重構,使其更具可測性,所以這不再是一個問題,但我仍然希望它能夠工作,因爲它看起來像一種有用的技術。非常感謝您的回覆。 – Samo 2012-02-15 17:27:33

+0

沒問題 - 如果你有一個更可測試的方法,那總是很好。 :)但是,我會拿走的是,我敢肯定,你可以以某種方式或另一種方式完成Moq想要的東西。有時到達那裏並不容易,但它非常靈活。 – 2012-02-15 17:34:35