Mocking HttpContextBase com Moq

Eu tenho um dispositivo de teste de unidade em que estou tentando testar um ControllerAction em um controlador de asp.net MVC que é usado para funções de associação em um aplicativo da web. Estou tentando zombar do HttpContext para os testes. O ControllerAction sob teste realmente define propriedades no HttpContext, como valores de session, valores de Response.Cookies, etc. Isso não é todo o código, mas aqui está uma amostra aproximada do teste que estou tentando executar :

[Test] public void ValidRegistrationDataSuccessfullyCreatesAndRegistersUser() { var context = new Mock() {DefaultValue = DefaultValue.Mock}; context.SetupAllProperties(); var provider = new Mock(new object[] {context.Object}); var controller = new AccountController(context.Object, provider.Object); // This just sets up a local FormCollection object with valid user data // in it to use to attempt the registration InitializeValidFormData(); ActionResult result = controller.Register(_registrationData); Assert.IsInstanceOfType(typeof(ViewResult), result); // Here is where I'd like to attempt to do Assertions against properties // of the HttpContext, like ensuring that a Session object called "User" // exists, and new auth cookie exists on the Response.Cookies collection. // So far I've been unable to successfully check the values of those properties. // I've been unsuccessful in getting those properties setup correctly on my // mock object so that my ControllerAction can actually *set* their values, // and that I can make assertions on them afterwards. The above code actually // generates a StackOverflowException (which I've reported) on the // context.SetupAllProperties() call. What am I doing wrong, or what do I need // to do to be able to set and assert on those context properties? } 

Não sei o que estou fazendo de errado, mas eu adoraria se alguém pudesse me apontar na direção certa e me mostrar como configurar esse object HttpContextBase falso, de modo que meu controlador possa definir valores em suas propriedades, e eu posso fazer isso afirmações sobre essas propriedades para garantir que o meu ControllerAction está fazendo o que eu preciso.

Estou me aproximando disso da maneira errada? Eu sei que MVC Controllers tem um ControllerContext que eu posso usar para definir valores para Session, etc, mas eu não consigo descobrir como algo como isso poderia ser ridicularizado sem injetá-lo. Existe alguma maneira de fazer isso? (Eu também preciso ser capaz de passar o contexto para o meu MembershipProvider também) Essa seria uma abordagem melhor?

Obrigado.

Estou usando uma versão de algum código que Steve Sanderson incluiu em seu livro Pro Asp.NET MVC … e atualmente estou tendo um dilema moral, se não há problema em postar o código aqui. Que tal eu comprometer com uma versão altamente despojada? 😉

Então, isso pode ser facilmente reutilizado, crie uma class semelhante à abaixo que você passará seu controlador. Isso irá configurar seus mocks e configurá-los para ControllerContext do seu controlador

 public class ContextMocks { public Moq.Mock HttpContext { get; set; } public Moq.Mock Request { get; set; } public RouteData RouteData { get; set; } public ContextMocks(Controller controller) { //define context objects HttpContext = new Moq.Mock(); HttpContext.Setup(x => x.Request).Returns(Request.Object); //you would setup Response, Session, etc similarly with either mocks or fakes //apply context to controller RequestContext rc = new RequestContext(HttpContext.Object, new RouteData()); controller.ControllerContext = new ControllerContext(rc, controller); } } 

E, em seguida, no seu método de teste, você criaria uma instância de ContextMocks e passaria o object do controlador que está testando:

 [Test] Public void test() { var mocks = new ContextMocks(controller); var req = controller.Request; //do some asserts on Request object } 

Parece muito semelhante aos exemplos de Craig, mas isso é com o Moq v3. Eu tenho que dar suporte a Steve Sanderson para isso – eu estou usando isso como uma base para testar todos os tipos de coisas tradicionalmente difíceis de testar: cookies, session, método de requisição, querystring e muito mais!

Aqui está como eu faço isso .

  public static HttpContextBase FakeHttpContext() { var context = new Mock(); var request = new Mock(); var response = new Mock(); var session = new Mock(); var server = new Mock(); var user = new Mock(); var identity = new Mock(); request.Expect(req => req.ApplicationPath).Returns("~/"); request.Expect(req => req.AppRelativeCurrentExecutionFilePath).Returns("~/"); request.Expect(req => req.PathInfo).Returns(string.Empty); response.Expect(res => res.ApplyAppPathModifier(It.IsAny())) .Returns((string virtualPath) => virtualPath); user.Expect(usr => usr.Identity).Returns(identity.Object); identity.ExpectGet(ident => ident.IsAuthenticated).Returns(true); context.Expect(ctx => ctx.Request).Returns(request.Object); context.Expect(ctx => ctx.Response).Returns(response.Object); context.Expect(ctx => ctx.Session).Returns(session.Object); context.Expect(ctx => ctx.Server).Returns(server.Object); context.Expect(ctx => ctx.User).Returns(user.Object); return context.Object; } 

Esta é uma versão aprimorada da biblioteca MvcMockHelpers lançada por Scott Hanselman . Este é o código Moq 2.0; a syntax é ligeiramente diferente em 3.