Mocking Asp.net-mvc Contexto del controlador

Entonces el contexto del controlador depende de algunos internos de asp.net. ¿Cuáles son algunas formas de simular estas pruebas para pruebas unitarias? Parece que es muy fácil bloquear pruebas con toneladas de configuración cuando solo necesito, por ejemplo, Request.HttpMethod para devolver “GET”.

He visto algunos ejemplos / ayudantes en las redes, pero algunos tienen fecha. Pensé que este sería un buen lugar para mantener lo último y mejor.

Estoy usando la última versión de rhino mocks

Usando MoQ se ve algo como esto:

var request = new Mock(); request.Expect(r => r.HttpMethod).Returns("GET"); var mockHttpContext = new Mock(); mockHttpContext.Expect(c => c.Request).Returns(request.Object); var controllerContext = new ControllerContext(mockHttpContext.Object , new RouteData(), new Mock().Object); 

Creo que la syntax de Rhino Mocks es similar.

Aquí hay una clase de prueba de unidad de muestra que usa MsTest y Moq que se burla de los objetos HttpRequest y HttpResponse. (.NET 4.0, ASP.NET MVC 3.0)

La acción del controlador obtiene valor de la solicitud y establece el encabezado http en los objetos de respuesta. Otros objetos de contexto http podrían burlarse de manera similar

 [TestClass] public class MyControllerTest { protected Mock HttpContextBaseMock; protected Mock HttpRequestMock; protected Mock HttpResponseMock; [TestInitialize] public void TestInitialize() { HttpContextBaseMock = new Mock(); HttpRequestMock = new Mock(); HttpResponseMock = new Mock(); HttpContextBaseMock.SetupGet(x => x.Request).Returns(HttpRequestMock.Object); HttpContextBaseMock.SetupGet(x => x.Response).Returns(HttpResponseMock.Object); } protected MyController SetupController() { var routes = new RouteCollection(); var controller = new MyController(); controller.ControllerContext = new ControllerContext(HttpContextBaseMock.Object, new RouteData(), controller); controller.Url = new UrlHelper(new RequestContext(HttpContextBaseMock.Object, new RouteData()), routes); return controller; } [TestMethod] public void IndexTest() { HttpRequestMock.Setup(x => x["x"]).Returns("1"); HttpResponseMock.Setup(x => x.AddHeader("name", "value")); var controller = SetupController(); var result = controller.Index(); Assert.AreEqual("1", result.Content); HttpRequestMock.VerifyAll(); HttpResponseMock.VerifyAll(); } } public class MyController : Controller { public ContentResult Index() { var x = Request["x"]; Response.AddHeader("name", "value"); return Content(x); } } 

Aquí hay un fragmento del enlace de Jason. Es lo mismo que el método de Phil pero usa rhino.

Nota: mockHttpContext.Request se repite para devolver mockRequest antes de que las partes internas de mockRequest se anulen. Creo que esta orden es obligatoria.

 // create a fake web context var mockHttpContext = MockRepository.GenerateMock(); var mockRequest = MockRepository.GenerateMock(); mockHttpContext.Stub(x => x.Request).Return(mockRequest); // tell the mock to return "GET" when HttpMethod is called mockRequest.Stub(x => x.HttpMethod).Return("GET"); var controller = new AccountController(); // assign the fake context var context = new ControllerContext(mockHttpContext, new RouteData(), controller); controller.ControllerContext = context; // act ... 

El procedimiento para esto parece haber cambiado ligeramente en MVC2 (estoy usando RC1). La solución de Phil Haack no funciona para mí si la acción requiere un método específico ( [HttpPost] , [HttpGet] ). Especulando en Reflector, parece que el método para verificar estos atributos ha cambiado. MVC ahora comprueba request.Headers , request.Form y request.QueryString para un valor de X-HTTP-Method-Override .

Si agrega burlas para estas propiedades, funciona:

 var request = new Mock(); request.Setup(r => r.HttpMethod).Returns("POST"); request.Setup(r => r.Headers).Returns(new NameValueCollection()); request.Setup(r => r.Form).Returns(new NameValueCollection()); request.Setup(r => r.QueryString).Returns(new NameValueCollection()); var mockHttpContext = new Mock(); mockHttpContext.Expect(c => c.Request).Returns(request.Object); var controllerContext = new ControllerContext(mockHttpContext.Object, new RouteData(), new Mock().Object); 

O puede hacer esto con Typemock Isolator sin necesidad de enviar un controlador falso en absoluto:

 Isolate.WhenCalled(()=>HttpContext.Request.HttpMethod).WillReturn("Get"); 

he terminado con esta especificación

 public abstract class Specification  where C: Controller { protected C controller; HttpContextBase mockHttpContext; HttpRequestBase mockRequest; protected Exception ExceptionThrown { get; private set; } [SetUp] public void Setup() { mockHttpContext = MockRepository.GenerateMock(); mockRequest = MockRepository.GenerateMock(); mockHttpContext.Stub(x => x.Request).Return(mockRequest); mockRequest.Stub(x => x.HttpMethod).Return("GET"); EstablishContext(); SetHttpContext(); try { When(); } catch (Exception exc) { ExceptionThrown = exc; } } protected void SetHttpContext() { var context = new ControllerContext(mockHttpContext, new RouteData(), controller); controller.ControllerContext = context; } protected T Mock() where T: class { return MockRepository.GenerateMock(); } protected abstract void EstablishContext(); protected abstract void When(); [TearDown] public virtual void TearDown() { } } 

y el jugo está aquí

 [TestFixture] public class When_invoking_ManageUsersControllers_Update :Specification  { private IUserRepository userRepository; FormCollection form; ActionResult result; User retUser; protected override void EstablishContext() { userRepository = Mock(); controller = new ManageUsersController(userRepository); retUser = new User(); userRepository.Expect(x => x.GetById(5)).Return(retUser); userRepository.Expect(x => x.Update(retUser)); form = new FormCollection(); form["IdUser"] = 5.ToString(); form["Name"] = 5.ToString(); form["Surename"] = 5.ToString(); form["Login"] = 5.ToString(); form["Password"] = 5.ToString(); } protected override void When() { result = controller.Edit(5, form); } [Test] public void is_retrieved_before_update_original_user() { userRepository.AssertWasCalled(x => x.GetById(5)); userRepository.AssertWasCalled(x => x.Update(retUser)); } } 

disfrutar

Me parece que ese largo procedimiento de burla es demasiada fricción.

La mejor manera que hemos encontrado, utilizando ASP.NET MVC en un proyecto real, es abstraer el HttpContext a una interfaz IWebContext que simplemente se transfiere. Entonces puedes burlarte del IWebContext sin ningún problema.

Aquí hay un ejemplo