[英]How can I test a custom DelegatingHandler in the ASP.NET MVC 4 Web API?
我在几个地方看到过这个问题,但没有看到任何好的答案。 由于我不得不自己做几次,我想我会发布我的解决方案。 如果你有更好的,请留言。
注意这是使用 ASP.NET MVC 4 Beta 2 版本的 Web API - 未来版本可能会改变!
更新:这仍然适用于 ASP.NET MVC 4 RC
在这种方法中,我创建了一个 TestHandler 并将其设置为InnerHandler
处理程序的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
非常可重用。
我只是在寻找同样的东西,但想出了一种更简洁的方法,不使用 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>
我创建了以下用于测试 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);
}
}
我也找到了这个答案,因为我有我的自定义处理程序,我想测试它我们正在使用 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<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
}
}
}
另一种选择可能是存根
public class TestingHandlerStub : DelegatingHandler
{
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public TestingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc)
{
_handlerFunc = handlerFunc;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return _handlerFunc(request, cancellationToken);
}
}
这是如何结合:
var handler = new YourCustomHandler()
{
InnerHandler = new TestingHandlerStub((r, c) =>
{
return Task.FromResult(httpResponseMessage);
})
};
var client = new HttpClient(handler);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.