with tutorial started route net mvc maphttproute attribute asp c# asp.net-mvc unit-testing asp.net-mvc-4 asp.net-web-api

tutorial - web api json c#



¿Cómo puedo probar un DelegatingHandler personalizado en la ASP.NET MVC 4 Web API? (4)

He visto esta pregunta surgir en algunos lugares, y no he visto ninguna gran respuesta. Como he tenido que hacerlo yo mismo algunas veces, pensé en publicar mi solución. Si tiene algo mejor, por favor publique.

NB Esto está usando la versión ASP.NET MVC 4 Beta 2 de la API Web: ¡las versiones futuras pueden cambiar!

Actualización: Esto todavía funciona en ASP.NET MVC 4 RC


Creé lo siguiente para probar DelegatingHandlers. Es útil para los manejadores que usan HttpRequestMessage.DependencyScope para resolver dependencias usando su marco de trabajo de IoC favorito, por ejemplo, un WindsorDependencyResolver con un 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); } }


Solo buscaba lo mismo, pero se me ocurrió un enfoque más conciso que no utilizaba el cliente http. Quería una prueba para afirmar que el manejador de mensajes consumió un componente de registro simulado. Realmente no necesitaba el controlador interno para funcionar, solo para "colgarlo" para satisfacer la prueba de la unidad. Funciona para mi propósito :)

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


También encontré esta respuesta porque tengo mi controlador personalizado y quiero probarlo. Estamos usando NUnit y Moq, así que creo que mi solución puede ser útil para alguien

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


En este enfoque, creo un TestHandler y lo configuro como la propiedad InnerHandler del controlador bajo prueba.

El controlador bajo prueba se puede pasar a un HttpClient - esto puede parecer poco intuitivo si está escribiendo un controlador del lado del servidor, pero esta es una gran manera de probar un manejador, se llamará de la misma manera que lo haría en un servidor.

El TestHandler simplemente devolverá un HTTP 200 de manera predeterminada, pero su constructor acepta una función que puede usar para hacer afirmaciones sobre el mensaje de solicitud pasado desde el controlador bajo prueba. Finalmente, puede hacer afirmaciones sobre el resultado de la llamada SendAsync del cliente.

Una vez que todo está configurado, llame a SendAsync en la instancia del cliente para invocar a su controlador. La solicitud pasará a su controlador, pasará esto al TestHandler (suponiendo que pasa la llamada) que luego devolverá una respuesta a su controlador.

El controlador de prueba se ve así:

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

Ejemplo de uso con un MyHandler imaginado bajo prueba. Utiliza NUnit para las afirmaciones .:

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

El comportamiento predeterminado de TestHandler probablemente esté bien para muchas pruebas y simplifica el código. La configuración del controlador bajo prueba se ve así:

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

Me gusta este enfoque porque mantiene todas las aserciones en el método de prueba, y el TestHandler es muy reutilizable.