Como faço para simular o HttpContext na ASP.NET MVC usando Moq?

101
[TestMethod]
public void Home_Message_Display_Unknown_User_when_coockie_does_not_exist()
{
    var context = new Mock<HttpContextBase>();
    var request = new Mock<HttpRequestBase>();
    context
        .Setup(c => c.Request)
        .Returns(request.Object);
    HomeController controller = new HomeController();

    controller.HttpContext = context; //Here I am getting an error (read only).
    ...
 }

meu controlador de base tem uma substituição do Initialize que obtém este requestContext. Estou tentando passar isso adiante, mas não estou fazendo algo certo.

protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
    base.Initialize(requestContext);
}

Onde posso obter mais informações sobre como simular meu RequestContext e HttpContext usando o Moq? Estou tentando simular cookies e o contexto geral.

Geo
fonte

Respostas:

61

HttpContext é somente leitura, mas, na verdade, é derivado do ControllerContext, que você pode definir.

 controller.ControllerContext = new ControllerContext( context.Object, new RouteData(), controller );
Tvanfosson
fonte
Este funcionou para mim, permitindo-me definir um HttpContext fictício no controlador.
Joel Malone
39

Crie uma solicitação, uma resposta e coloque ambas em HttpContext:

HttpRequest httpRequest = new HttpRequest("", "http://mySomething/", "");
StringWriter stringWriter = new StringWriter();
HttpResponse httpResponse = new HttpResponse(stringWriter);
HttpContext httpContextMock = new HttpContext(httpRequest, httpResponse);
0100110010101
fonte
A questão é sobre as classes * Base, ou seja, HttpRequestBase, não HttpRequest - não tenho certeza de por que ambas são necessárias e, mais irritante ainda, são "seladas". Não é possível definir LogonUserIdentity :(
Chris Kimpton,
Se eles forem encaminhados para minha referência, ainda é possível via remoting, então não deve ser um problema.
0100110010101
1
@ChrisKimpton: Como último recurso, sempre há reflexão ;-)
Oliver
Isso funciona ao anexá-lo ao controlador, assim: controlador.ControllerContext = new ControllerContext (new HttpContextWrapper (httpContextMock), new RouteData (), controlador);
Andreas Vendel
sim. você realmente pode definir .LogonUserIdentity - _request.Setup (n => n.LogonUserIdentity) .Returns ((WindowsIdentity.GetCurrent));
KevinDeus de
12

Obrigado, usuário 0100110010101.

Funcionou para mim e aqui eu tive um problema ao escrever o caso de teste para o código abaixo:

 var currentUrl = Request.Url.AbsoluteUri;

E aqui estão as linhas que resolveram o problema

HomeController controller = new HomeController();
//Mock Request.Url.AbsoluteUri 
HttpRequest httpRequest = new HttpRequest("", "http://mySomething", "");
StringWriter stringWriter = new StringWriter();
HttpResponse httpResponse = new HttpResponse(stringWriter);
HttpContext httpContextMock = new HttpContext(httpRequest, httpResponse);
controller.ControllerContext = new ControllerContext(new HttpContextWrapper(httpContextMock), new RouteData(), controller);

Pode ser útil para os outros.

Chandan Kumar
fonte
Não consigo usar o tipo HttpRequest - é outra coisa agora?
Vincent Buscarello
1
Isso não é útil porque todos os campos em HttpRequest são imutáveis
A br
6

Aqui está um exemplo de como você pode configurar isso: Mocking HttpContext HttpRequest e HttpResponse para UnitTests (usando Moq)

Observe os métodos de extensão que realmente ajudam a simplificar o uso dessas classes de simulação:

var mockHttpContext = new API_Moq_HttpContext();

var httpContext = mockHttpContext.httpContext();

httpContext.request_Write("<html><body>".line()); 
httpContext.request_Write("   this is a web page".line());  
httpContext.request_Write("</body></html>"); 

return httpContext.request_Read();

Aqui está um exemplo de como escrever um Teste de Unidade usando moq para verificar se um HttpModule está funcionando conforme o esperado: Teste de Unidade para HttpModule usando Moq para envolver HttpRequest

Update: esta API foi refatorada para

Dinis cruz
fonte
Links quebrados - inclua o código em sua resposta
Hades
5

Veja como usei o ControllerContext para passar um caminho de aplicativo falso:

[TestClass]
public class ClassTest
{
    private Mock<ControllerContext> mockControllerContext;
    private HomeController sut;

    [TestInitialize]
    public void TestInitialize()
    {
        mockControllerContext = new Mock<ControllerContext>();
        sut = new HomeController();
    }
    [TestCleanup]
    public void TestCleanup()
    {
        sut.Dispose();
        mockControllerContext = null;
    }
    [TestMethod]
    public void Index_Should_Return_Default_View()
    {

        // Expectations
        mockControllerContext.SetupGet(x => x.HttpContext.Request.ApplicationPath)
            .Returns("/foo.com");
        sut.ControllerContext = mockControllerContext.Object;

        // Act
        var failure = sut.Index();

        // Assert
        Assert.IsInstanceOfType(failure, typeof(ViewResult), "Index() did not return expected ViewResult.");
    }
}
Xolartek
fonte
1
Por que você precisa passar um caminho de aplicativo falso?
the_law
O código MVC irá executá-lo e lançar uma exceção nula se não estiver lá.
Joshua Ramirez