Подтвердить что ты не робот

Как я могу протестировать пользовательский DelegatingHandler в ASP.NET MVC 4 Web API?

Я видел, как этот вопрос появился в нескольких местах и ​​не видел больших ответов. Поскольку я должен был сделать это сам несколько раз, я думал, что опубликую свое решение. Если у вас что-то лучше, отправьте сообщение.

N.B. Это использование ASP.NET MVC 4 Beta 2 версии Web API - будущие версии могут измениться!

Обновление: это все еще работает в ASP.NET MVC 4 RC

4b9b3361

Ответ 1

В этом подходе я создаю 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 очень многократно используется.

Ответ 2

Я просто искал то же самое, но придумал более сжатый подход, который не использовал http-клиент. Мне нужен тест, чтобы утверждать, что обработчик сообщений потребляет издеваемую составляющую регистрации. Мне не нужен внутренний обработчик для работы, просто чтобы "заглушить" его, чтобы удовлетворить unit test. Работает для моих целей:)

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

Ответ 3

Я создал следующее для тестирования 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<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

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