Problemas que se burlan de la solicitud de MVC ControllerContext
-
07-07-2019 - |
Pregunta
Soy bastante nuevo en las pruebas y burlas y estoy tratando de escribir una prueba que garantice que mi lógica de validación esté configurando los errores de ModelState correctamente.
Lo que estoy viendo es que el controller.ControllerContext.HttpContext.Request se establece la primera vez que lo verifico, pero cada vez después de eso, Request es nulo.
Esto está causando una excepción de referencia nula en el método PopulateDictionary de la clase * ValueProviderDictionary * en la fuente MVC porque se accede al objeto de solicitud varias veces en ese método sin garantizar que la solicitud no sea nula.
Estoy pegando varias técnicas y ayudantes que he encontrado al investigar cómo superar algunos de los problemas que he encontrado hasta ahora, así que en este momento no estoy seguro de dónde podría haber introducido el problema.
¿Estoy usando objetos simulados incorrectamente aquí?
Prueba fallida
//Test
public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel()
{
//Arrange
DataAccessFactoryMocks.MockAllDaos();
var controller = new FooController();
var testFormCollection = new NameValueCollection();
testFormCollection.Add("foo.CustomerID", "3");
testFormCollection.Add("_fooForm", SerializationUtils.Serialize(new FooModel()));
var mockHttpContext = new MockHttpContext(controller, "POST", testFormCollection, null);
//Accessor used to run the protected OnActionExecuting method in my controller
var accessor = new FooControllerAccessor(controller);
//Request is set, assertion passes
Assert.IsNotNull(controller.ControllerContext.HttpContext.Request.Form);
//Request is null when accessing the property a second time, assertion fails
Assert.IsNotNull(controller.ControllerContext.HttpContext.Request.QueryString);
//Act
accessor.OnActionExecuting(new ActionExecutingContext(controller.ControllerContext, MockRepository.GenerateStub<ActionDescriptor>(), new Dictionary<string, object>()));
//Assert
Assert.That(controller.ModelState.IsValid == false);
}
Ayudante de prueba
//Test helper to create httpcontext and set controller context accordingly
public class MockHttpContext
{
public HttpContextBase HttpContext { get; private set; }
public HttpRequestBase Request { get; private set; }
public HttpResponseBase Response { get; private set; }
public RouteData RouteData { get; private set; }
public MockHttpContext(Controller onController)
{
//Setup the common context components and their relationships
HttpContext = MockRepository.GenerateMock<HttpContextBase>();
Request = MockRepository.GenerateMock<HttpRequestBase>();
Response = MockRepository.GenerateMock<HttpResponseBase>();
//Setup the context, request, response relationship
HttpContext.Stub(c => c.Request).Return(Request);
HttpContext.Stub(c => c.Response).Return(Response);
Request.Stub(r => r.Cookies).Return(new HttpCookieCollection());
Response.Stub(r => r.Cookies).Return(new HttpCookieCollection());
Request.Stub(r => r.QueryString).Return(new NameValueCollection());
Request.Stub(r => r.Form).Return(new NameValueCollection());
//Apply the context to the suppplied controller
var rc = new RequestContext(HttpContext, new RouteData());
onController.ControllerContext = new ControllerContext(rc, onController);
}
public MockHttpContext(Controller onController, string httpRequestType, NameValueCollection form, NameValueCollection querystring)
{
//Setup the common context components and their relationships
HttpContext = MockRepository.GenerateMock<HttpContextBase>();
Request = MockRepository.GenerateMock<HttpRequestBase>();
Response = MockRepository.GenerateMock<HttpResponseBase>();
//Setup request type based on parameter value
Request.Stub(r => r.RequestType).Return(httpRequestType);
//Setup the context, request, response relationship
HttpContext.Stub(c => c.Request).Return(Request);
HttpContext.Stub(c => c.Response).Return(Response);
Request.Stub(r => r.Cookies).Return(new HttpCookieCollection());
Response.Stub(r => r.Cookies).Return(new HttpCookieCollection());
Request.Stub(r => r.QueryString).Return(querystring);
Request.Stub(r => r.Form).Return(form);
//Apply the context to the suppplied controller
var rc = new RequestContext(HttpContext, new RouteData());
onController.ControllerContext = new ControllerContext(rc, onController);
}
}
Prueba de trabajo con MvcContrib.TestHelper
public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel()
{
//Arrange
DataAccessFactoryMocks.MockAllDaos();
TestControllerBuilder builder = new TestControllerBuilder();
builder.Form.Add("fooModel.CustomerID", "3");
builder.HttpContext.Request.Stub(r => r.RequestType).Return("POST");
FooController controller = builder.CreateController<FooController>();
var accessor = new FooControllerAccessor(controller);
//Act
accessor.OnActionExecuting(new ActionExecutingContext(controller.ControllerContext, MockRepository.GenerateStub<ActionDescriptor>(), new Dictionary<string, object>()));
//Assert
Assert.IsFalse(controller.ModelState.IsValid);
}
Solución
Te sugiero que uses el excelente MVCContrib TestHelper para la unidad probar sus controladores ASP.NET MVC con Rhino Mocks. Verá una simplificación drástica de sus pruebas unitarias y una mayor legibilidad.
Otros consejos
Lo que entiendo de su pregunta es que la burla de ControllerContext también podría reemplazarse por un objeto auxiliar porque el objetivo no es probar el comportamiento de ControllerContext. Además, no estoy muy seguro de por qué necesita un FooControllerAccessor, mientras que su única preocupación es afirmar el ModelState, así que lo dejé aquí:
public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel()
{
// Arrange
var action = new FooController()
.Action("index")
.RequestData(new Dictionary<string, object>()
{
{"foo.CustomerID", 3},
{"_fooForm", new FooModel()}
});
//Act
var modelState = action.ValidateRequest();
//Assert
Assert.That(modelState.IsValid == false);
}
Para usar este código, debe instalar Xania.AspNet.Simulator (al momento de escribir v1.4.0-beta5) funciona para Mvc4 y Mvc5
PM > install-package Xania.AspNet.Simulator -Pre
Para obtener más ejemplos, consulte lo siguiente:
- https://github.com/ibrahimbensalah/Xania.AspNet
- http://www.codeproject.com/ Tips / 1027591 / Desata la plantilla de poder de la maquinilla de afeitar
- http: // www. codeproject.com/Tips/850277/ASP-NET-MVC-End-to-End-Integration-Testing
- http://www.codeproject.com/Tips/1005313/ Prueba de caja blanca con selenio