如何在ASP.NET MVC 4 Web API中测试自定义DelegatingHandler?

我已经看到这个问题出现在一些地方,没有看到任何好的答案。 因为我必须自己做几次,我以为我会发布我的解决方案。 如果你有更好的,请发布。

NB这是使用ASP.NET MVC 4 Beta 2版本的Web API – 未来的版本可能会改变!

更新:这仍然适用于ASP.NET MVC 4 RC

在这种方法中,我创建了一个TestHandler并将其设置为测试中的处理程序的InnerHandler属性。

然后可以将测试中的处理程序传递给HttpClient – 如果您正在编写服务器端处理程序,这可能看起来不直观,但这实际上是一种非常轻量级的方法来测试处理程序 – 它将以相同的方式调用它会在服务器中。

TestHandler默认只返回一个HTTP 200,但它的构造函数接受一个函数,你可以使用该函数来断言从被测试的处理程序传入的请求消息。 最后,您可以对来自客户端的SendAsync调用的结果进行断言。

设置完所有内容后,在客户端实例上调用SendAsync以调用您的处理程序。 请求将被传递到您的处理程序,它将传递给TestHandler(假设它传递调用),然后将响应返回给您的处理程序。

测试处理程序如下所示:

 public class TestHandler : DelegatingHandler { private readonly Func> _handlerFunc; public TestHandler() { _handlerFunc = (r, c) => Return200(); } public TestHandler(Func> handlerFunc) { _handlerFunc = handlerFunc; } protected override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { return _handlerFunc(request, cancellationToken); } public static Task 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非常可重用。

我只是在寻找相同的东西,但想出了一个更简洁的方法,没有使用http客户端。 我想要一个测试来断言消息处理程序消耗了一个模拟的日志记录组件。 我真的不需要内部处理程序来运行,只是为了“存根”它以满足unit testing。 适合我的目的:)

 //ARRANGE var logger = new Mock(); var handler= new ServiceLoggingHandler(logger.Object); var request = ControllerContext.CreateHttpRequest(Guid.NewGuid(), "http://test",HttpMethod.Get); handler.InnerHandler = new Mock(MockBehavior.Loose).Object; request.Content = new ObjectContent(Company.CreateCompanyDTO(), new JsonMediaTypeFormatter()); var invoker = new HttpMessageInvoker(handler); //ACT var result = invoker.SendAsync(request, new System.Threading.CancellationToken()).Result; //ASSERT  

我为测试DelegatingHandlers创建了以下内容。 对于使用HttpRequestMessage.DependencyScope使用您喜欢的IoC框架解析依赖关系的处理程序非常有用,例如WindsorDependencyResolver和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 SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request == null) { throw new ArgumentNullException("request"); } request.Properties["MS_HttpConfiguration"] = this.configuration; return base.SendAsync(request, cancellationToken); } } 

我也找到了这个答案,因为我有我的自定义处理程序,我想测试它我们正在使用NUnit和Moq,所以我认为我的解决方案对某些人有帮助

  using Moq; using Moq.Protected; using NUnit.Framework; namespace Unit.Tests { [TestFixture] public sealed class Tests1 { private HttpClient _client; private HttpRequestMessage _httpRequest; private Mock _testHandler; private MyCustomHandler _subject;//MyCustomHandler inherits DelegatingHandler [SetUp] public void Setup() { _httpRequest = new HttpRequestMessage(HttpMethod.Get, "/someurl"); _testHandler = new Mock(); _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>("SendAsync", _httpRequest, ItExpr.IsAny()) .Callback( (Action)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()); // 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 } } }