Jak mogę mock HttpContext w ASP.NET MVC przy użyciu 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).
    ...
 }

mój kontroler podstawowy ma nadpisanie właściwości Initialize, które pobierają ten requestContext. Próbuję to przekazać, ale nie robię czegoś dobrze.

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

Gdzie mogę uzyskać więcej informacji na temat mockowania mojego RequestContext i HttpContext przy użyciu Moq? Próbuję kpić z ciasteczek i ogólnego kontekstu.

Geo
źródło

Odpowiedzi:

61

HttpContext jest tylko do odczytu, ale w rzeczywistości pochodzi z ControllerContext, który można ustawić.

 controller.ControllerContext = new ControllerContext( context.Object, new RouteData(), controller );
tvanfosson
źródło
Ten zadziałał dla mnie, pozwalając mi ustawić pozorny HttpContext na kontrolerze.
Joel Malone
39

Utwórz żądanie, odpowiedz i umieść je oba w HttpContext:

HttpRequest httpRequest = new HttpRequest("", "http://mySomething/", "");
StringWriter stringWriter = new StringWriter();
HttpResponse httpResponse = new HttpResponse(stringWriter);
HttpContext httpContextMock = new HttpContext(httpRequest, httpResponse);
0100110010101
źródło
Pytanie dotyczy klas * Base, czyli HttpRequestBase, a nie HttpRequest - sam nie wiem, dlaczego obie są potrzebne, a jeszcze bardziej denerwujące, że są „zapieczętowane”. Nie ma możliwości ustawienia LogonUserIdentity :(
Chris Kimpton,
Jeśli są skierowane do mojego odniesienia, nadal jest to możliwe za pośrednictwem zdalnego połączenia, więc nie powinno to stanowić problemu.
0100110010101
1
@ChrisKimpton: W ostateczności zawsze jest refleksja ;-)
Oliver
Działa to po dołączeniu go do kontrolera, na przykład: controller.ControllerContext = new ControllerContext (new HttpContextWrapper (httpContextMock), new RouteData (), controller);
Andreas Vendel,
tak. rzeczywiście możesz ustawić .LogonUserIdentity - _request.Setup (n => n.LogonUserIdentity) .Returns ((WindowsIdentity.GetCurrent));
KevinDeus
12

Dzięki użytkowniku 0100110010101.

U mnie zadziałało i tutaj miałem problem podczas pisania testu case dla poniższego kodu:

 var currentUrl = Request.Url.AbsoluteUri;

A oto kwestie, które rozwiązały problem

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

Może być pomocny dla innych.

Chandan Kumar
źródło
Nie mogę używać typu HttpRequest - czy to teraz coś innego?
Vincent Buscarello,
1
Nie jest to przydatne, ponieważ wszystkie pola w HttpRequest są niezmienne
A br
6

Oto jak użyłem ControllerContext do przekazania fałszywej ścieżki do aplikacji:

[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
źródło
1
Dlaczego musiałeś przejść fałszywą ścieżkę aplikacji?
the_law
Kod MVC wykona go i zgłosi wyjątek zerowy, jeśli go tam nie ma.
Joshua Ramirez
5

Oto przykład, jak możesz to ustawić: Mocking HttpContext HttpRequest i HttpResponse for UnitTests (using Moq)

Zwróć uwagę na metody rozszerzające, które naprawdę pomagają uprościć użycie tych szyderczych klas:

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

Oto przykład, jak napisać test jednostkowy przy użyciu moq, aby sprawdzić, czy HttpModule działa zgodnie z oczekiwaniami: Test jednostkowy dla HttpModule przy użyciu Moq do zawijania HttpRequest

Aktualizacja: ten interfejs API został zmieniony na

Dinis Cruz
źródło
Linki są zepsute - prosimy o podanie kodu w odpowiedzi
Hades