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

Jam*_*rld 32 c# asp.net-mvc unit-testing asp.net-mvc-4 asp.net-web-api

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

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

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

Jam*_*rld 48

在这种方法中,我创建了一个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));
    }
}
Run Code Online (Sandbox Code Playgroud)

想象中的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));
Run Code Online (Sandbox Code Playgroud)

TestHandler的默认行为可能适用于许多测试,并使代码更简单.然后,测试中的处理程序的设置如下所示:

var handler = new MyHandler();
handler.InnerHandler = new TestHandler();
Run Code Online (Sandbox Code Playgroud)

我喜欢这种方法,因为它保留了测试方法中的所有断言,并且TestHandler非常可重用.

  • 您通常不应该使用“Task.Factory.StartNew()”,它对于测试来说可能没问题,但由于调度程序和同步上下文的不那么明显的性质,通常这是一种搬起石头砸自己脚的简单方法。对于这种情况,我建议使用“Task.FromResult()”。 (2认同)

小智 5

我只是在寻找相同的东西,但想出了一个更简洁的方法,没有使用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>
Run Code Online (Sandbox Code Playgroud)