2012-03-20 28 views
27

Tôi đã nhìn thấy câu hỏi này xuất hiện ở một vài nơi và không thấy bất kỳ câu trả lời tuyệt vời nào. Như tôi đã phải làm điều này bản thân mình một vài lần, tôi nghĩ rằng tôi muốn gửi giải pháp của tôi. Nếu bạn có bất cứ điều gì tốt hơn, xin vui lòng gửi bài.Làm thế nào tôi có thể kiểm tra DelegatingHandler tùy chỉnh trong ASP.NET MVC 4 Web API?

N.B. Đây là phiên bản ASP.NET MVC 4 Beta 2 của Web API - các phiên bản trong tương lai có thể thay đổi!

Cập nhật: Đây vẫn hoạt động trong ASP.NET MVC 4 RC

Trả lời

42

Trong phương pháp này, tôi tạo ra một TestHandler và thiết lập nó như là tài sản của InnerHandler xử lý được kiểm tra. Trình xử lý được kiểm tra sau đó có thể được chuyển đến một số HttpClient - điều này có vẻ không trực quan nếu bạn đang viết một trình xử lý phía máy chủ, nhưng đây thực sự là một cách nhẹ để kiểm tra trình xử lý - nó sẽ được gọi giống như cách nó sẽ làm trong một máy chủ.

TestHandler sẽ chỉ trả lại HTTP 200 theo mặc định, nhưng hàm tạo của nó chấp nhận một hàm bạn có thể sử dụng để thực hiện xác nhận về thông báo yêu cầu đã qua từ trình xử lý đang được kiểm tra. Cuối cùng, bạn có thể xác nhận kết quả của cuộc gọi SendAsync từ máy khách.

Sau khi mọi thứ được thiết lập, hãy gọi SendAsync trên phiên bản ứng dụng khách để gọi trình xử lý của bạn. Yêu cầu sẽ được chuyển vào trình xử lý của bạn, nó sẽ chuyển nó cho TestHandler (giả sử nó truyền cuộc gọi), sau đó nó sẽ trả về một câu trả lời cho trình xử lý của bạn.

handler

Xét nghiệm này trông như thế này:

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)); 
    } 
} 

Ví dụ sử dụng với một tưởng tượng MyHandler dưới kiểm tra. Sử dụng NUnit cho các xác nhận .:

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)); 

Hành vi mặc định của TestHandler có thể tốt cho nhiều thử nghiệm và làm cho mã đơn giản hơn. Quá trình cài đặt các trình điều khiển được kiểm tra sau đó trông như thế này:

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

Tôi thích phương pháp này vì nó giữ tất cả các khẳng định trong phương pháp kiểm tra, và TestHandler rất tái sử dụng.

+0

cách bạn xử lý đối tượng yêu cầu tại đây. request.createresponse thất bại vì không có httpconfiguration.vì vậy nếu bạn thêm một cái bạn có thể nhận được, nhưng nếu bạn quan tâm đến các thuộc tính yêu cầu khác được liên kết với httprequestmessage bạn đã tạo ra thì sao? – Steve

+0

Mục tiêu của tôi ở đây là hoàn toàn kiểm tra trình xử lý; vì vậy không phải là gợi ý của bạn về việc thêm HttpConfiguration đủ? Kịch bản của bạn là gì? –

+0

Tôi đã phải thêm trình bao bọc xung quanh request.content, request.properties và request.getroutedata. tất cả là tốt trong thử nghiệm landia – Steve

0

Tôi đã tạo thông tin sau để thử nghiệm DelegatingHandlers. Nó rất hữu ích cho các trình xử lý sử dụng HttpRequestMessage.DependencyScope để giải quyết các phụ thuộc bằng cách sử dụng khung công tác IoC yêu thích của bạn, ví dụ: một WindsorDependencyResolver với một WindsorContainer:

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

Tôi chỉ tìm kiếm những điều tương tự, nhưng đã đưa ra một cách tiếp cận ngắn gọn hơn mà không sử dụng khách hàng http. Tôi muốn một thử nghiệm để xác nhận xử lý tin nhắn tiêu thụ một thành phần ghi chép giả mạo. Tôi đã không thực sự cần xử lý bên trong để hoạt động, chỉ để "cuống" nó ra để đáp ứng các bài kiểm tra đơn vị. Làm việc cho mục đích của tôi :)

//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

Tôi cũng tìm thấy câu trả lời này bởi vì tôi có xử lý tuỳ chỉnh của tôi và tôi muốn thử nghiệm nó Chúng tôi đang sử dụng NUnit và Moq, vì vậy tôi nghĩ rằng giải pháp của tôi có thể hữu ích cho một ai đó

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 
     } 
    } 
} 
Các vấn đề liên quan