2012-03-20 79 views

回答

42

在這種方法中,我創建了一個TestHandler並將其設置爲處理程序的被測InnerHandler財產。

被測試的處理程序然後可以被傳遞給HttpClient - 如果您正在編寫服務器端處理程序,這看起來可能不直觀,但這實際上是測試處理程序的一種很好的輕量級方式 - 它將被調用就像在服務器中一樣。

TestHandler默認會返回一個HTTP 200,但它的構造函數接受一個函數,可以使用這個函數來對來自被測試的處理程序的 傳入的請求消息進行斷言。最後,您可以對來自客戶端的SendAsync調用的結果進行斷言。

一旦完成所有設置,請在客戶端實例上調用SendAsync以調用您的處理程序。請求將傳遞到你的處理程序中,它將把它傳遞給TestHandler(假設它通過調用),然後返回一個響應給你的處理程序。

測試處理機是這樣的:

public class TestHandler : DelegatingHandler 
{ 
    private readonly Func<HttpRequestMessage, 
     CancellationToken, Task<HttpResponseMessage>> _handlerFunc; 

    public TestHandler() 
    { 
     _handlerFunc = (r, c) => Return200(); 
    } 

    public TestHandler(Func<HttpRequestMessage, 
     CancellationToken, Task<HttpResponseMessage>> handlerFunc) 
    { 
     _handlerFunc = handlerFunc; 
    } 

    protected override Task<HttpResponseMessage> SendAsync(
     HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     return _handlerFunc(request, cancellationToken);    
    } 

    public static Task<HttpResponseMessage> Return200() 
    { 
     return Task.Factory.StartNew(
      () => new HttpResponseMessage(HttpStatusCode.OK)); 
    } 
} 

實施例使用與被測試的假想MyHandler。使用NUnit的爲斷言:

var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://test.com"); 
httpRequestMessage.Headers.Add("username", "test"); 

var handler = new MyHandler() 
{ 
    InnerHandler = new TestHandler((r,c) => 
    { 
     Assert.That(r.Headers.Contains("username")); 
     return TestHandler.Return200(); 
    }) 
}; 

var client = new HttpClient(handler); 
var result = client.SendAsync(httpRequestMessage).Result; 

Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.OK)); 

TestHandler的默認行爲可能是許多試驗和罰款,使代碼更簡單。處理程序的測試設置,然後看起來是這樣的:

var handler = new MyHandler(); 
handler.InnerHandler = new TestHandler(); 

我喜歡這種方法,因爲它使測試方法中的所有斷言,和TestHandler非常重用的。

+0

你如何處理這裏的請求對象。由於沒有httpconfiguration,request.createresponse失敗。所以如果你添加一個你可以得到的,但是如果你對與你創建的httprequestmessage相關的其他請求屬性感興趣呢? – Steve 2013-01-18 01:53:25

+0

我這裏的目標純粹是爲了測試處理程序;那麼你的建議是否足夠添加HttpConfiguration?你的場景是什麼? – 2013-01-24 00:52:24

+0

我不得不在request.content,request.properties和request.getroutedata中添加包裝。一切都很好,在測試landia – Steve 2013-01-24 01:11:49

0

我創建了以下用於測試DelegatingHandlers。對於使用HttpRequestMessage.DependencyScope來處理使用您最喜歡的IoC框架的依賴關係的處理程序非常有用,例如有WindsorContainer一個WindsorDependencyResolver:

public class UnitTestHttpMessageInvoker : HttpMessageInvoker 
    { 
     private readonly HttpConfiguration configuration; 

     public UnitTestHttpMessageInvoker(HttpMessageHandler handler, IDependencyResolver resolver) 
     : base(handler, true) 
     { 
      this.configuration = new HttpConfiguration(); 
      configuration.DependencyResolver = resolver; 
     } 

     [DebuggerNonUserCode] 
     public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
     { 
      if (request == null) 
      { 
       throw new ArgumentNullException("request"); 
      } 

      request.Properties["MS_HttpConfiguration"] = this.configuration; 
      return base.SendAsync(request, cancellationToken); 
     } 
    } 
4

我只是在尋找同樣的事情,但與沒有使用HTTP客戶端更簡潔的方式上來。我想要一個測試來斷言消息處理程序使用了一個模擬日誌記錄組件。我並不是真的需要內部處理函數來運行,只是爲了「存根」它來滿足單元測試。適用於我的目的:)

//ARRANGE 
     var logger = new Mock<ILogger>(); 
     var handler= new ServiceLoggingHandler(logger.Object); 
     var request = ControllerContext.CreateHttpRequest(Guid.NewGuid(), "http://test",HttpMethod.Get); 

     handler.InnerHandler = new Mock<HttpMessageHandler>(MockBehavior.Loose).Object; 

     request.Content = new ObjectContent<CompanyRequest>(Company.CreateCompanyDTO(), new JsonMediaTypeFormatter()); 
     var invoker = new HttpMessageInvoker(handler); 

     //ACT 
     var result = invoker.SendAsync(request, new System.Threading.CancellationToken()).Result; 

//ASSERT 
<Your assertion> 
0

我也發現了這個答案,因爲我有我的自定義處理程序,我想測試一下 我們使用NUnit和起訂量,所以我覺得我的解決方案可能是有人

有幫助
using Moq; 
    using Moq.Protected; 
    using NUnit.Framework; 
namespace Unit.Tests 
{ 
    [TestFixture] 
    public sealed class Tests1 
    { 
     private HttpClient _client; 
     private HttpRequestMessage _httpRequest; 
     private Mock<DelegatingHandler> _testHandler; 

     private MyCustomHandler _subject;//MyCustomHandler inherits DelegatingHandler 

     [SetUp] 
     public void Setup() 
     { 
      _httpRequest = new HttpRequestMessage(HttpMethod.Get, "/someurl"); 
      _testHandler = new Mock<DelegatingHandler>(); 

      _subject = new MyCustomHandler // create subject 
      { 
       InnerHandler = _testHandler.Object //initialize InnerHandler with our mock 
      }; 

      _client = new HttpClient(_subject) 
      { 
       BaseAddress = new Uri("http://localhost") 
      }; 
     } 

     [Test] 
     public async Task Given_1() 
     { 
      var mockedResult = new HttpResponseMessage(HttpStatusCode.Accepted); 

      void AssertThatRequestCorrect(HttpRequestMessage request, CancellationToken token) 
      { 
       Assert.That(request, Is.SameAs(_httpRequest)); 
       //... Other asserts 
      } 

      // setup protected SendAsync 
      // our MyCustomHandler will call SendAsync internally, and we want to check this call 
      _testHandler 
       .Protected() 
       .Setup<Task<HttpResponseMessage>>("SendAsync", _httpRequest, ItExpr.IsAny<CancellationToken>()) 
       .Callback(
        (Action<HttpRequestMessage, CancellationToken>)AssertThatRequestCorrect) 
       .ReturnsAsync(mockedResult); 

      //Act 
      var actualResponse = await _client.SendAsync(_httpRequest); 

      //check that internal call to SendAsync was only Once and with proper request object 
      _testHandler 
       .Protected() 
       .Verify("SendAsync", Times.Once(), _httpRequest, ItExpr.IsAny<CancellationToken>()); 

      // if our custom handler modifies somehow our response we can check it here 
      Assert.That(actualResponse.IsSuccessStatusCode, Is.True); 
      Assert.That(actualResponse, Is.EqualTo(mockedResult)); 
      //...Other asserts 
     } 
    } 
} 
相關問題