it-swarm.com.de

Mock HttpContext.Current in Test Init-Methode

Ich versuche, einer von mir erstellten ASP.NET MVC-Anwendung Komponententests hinzuzufügen. In meinen Unit-Tests verwende ich den folgenden Code:

[TestMethod]
public void IndexAction_Should_Return_View() {
    var controller = new MembershipController();
    controller.SetFakeControllerContext("TestUser");

    ...
}

Mit den folgenden Helfern können Sie den Controller-Kontext verspotten:

public static class FakeControllerContext {
    public static HttpContextBase FakeHttpContext(string username) {
        var context = new Mock<HttpContextBase>();

        context.SetupGet(ctx => ctx.Request.IsAuthenticated).Returns(!string.IsNullOrEmpty(username));

        if (!string.IsNullOrEmpty(username))
            context.SetupGet(ctx => ctx.User.Identity).Returns(FakeIdentity.CreateIdentity(username));

        return context.Object;
    }

    public static void SetFakeControllerContext(this Controller controller, string username = null) {
        var httpContext = FakeHttpContext(username);
        var context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
        controller.ControllerContext = context;
    }
}

Diese Testklasse erbt von einer Basisklasse mit den folgenden Eigenschaften:

[TestInitialize]
public void Init() {
    ...
}

Innerhalb dieser Methode wird eine Bibliothek aufgerufen (über die ich keine Kontrolle habe), die versucht, den folgenden Code auszuführen:

HttpContext.Current.User.Identity.IsAuthenticated

Jetzt können Sie wahrscheinlich das Problem sehen. Ich habe den gefälschten HttpContext gegen den Controller gesetzt, aber nicht in dieser Basis-Init-Methode. Unit Testing/Mocking ist für mich sehr neu, deshalb möchte ich sicherstellen, dass ich es richtig mache. Was ist der richtige Weg für mich, den HttpContext so zu verspotten, dass er von meinem Controller und allen Bibliotheken, die in meiner Init-Methode aufgerufen werden, gemeinsam genutzt wird?.

166
nfplee

HttpContext.Current Gibt eine Instanz von System.Web.HttpContext zurück, die System.Web.HttpContextBase nicht erweitert. HttpContextBase wurde später hinzugefügt, um die schwer zu verspottende Adresse HttpContext anzusprechen. Die beiden Klassen sind grundsätzlich nicht miteinander verbunden ( HttpContextWrapper wird als Adapter zwischen ihnen verwendet).

Glücklicherweise ist HttpContext selbst gerade so fälschbar, dass Sie IPrincipal (Benutzer) und IIdentity ersetzen.

Der folgende Code wird auch in einer Konsolenanwendung wie erwartet ausgeführt:

HttpContext.Current = new HttpContext(
    new HttpRequest("", "http://tempuri.org", ""),
    new HttpResponse(new StringWriter())
    );

// User is logged in
HttpContext.Current.User = new GenericPrincipal(
    new GenericIdentity("username"),
    new string[0]
    );

// User is logged out
HttpContext.Current.User = new GenericPrincipal(
    new GenericIdentity(String.Empty),
    new string[0]
    );
341
Richard Szalay

Unter Test Init erledigt auch der Job.

[TestInitialize]
public void TestInit()
{
  HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
  YourControllerToBeTestedController = GetYourToBeTestedController();
}
29
PUG

Ich weiß, dass dies ein älteres Thema ist, aber eine MVC-Anwendung für Komponententests zu verspotten, ist etwas, was wir sehr regelmäßig tun.

Ich wollte nur meine Erfahrungen mit dem Verspotten einer MVC 3-Anwendung mithilfe von Moq 4 nach dem Upgrade auf Visual Studio 2013 hinzufügen. Keiner der Komponententests funktionierte im Debug-Modus, und im HttpContext wurde beim Versuch, einen Blick auf die Variablen zu werfen, "Ausdruck konnte nicht ausgewertet werden" angezeigt .

Es stellte sich heraus, dass Visual Studio 2013 Probleme bei der Bewertung einiger Objekte hat. Um das Debuggen von verspotteten Webanwendungen wieder zum Laufen zu bringen, musste ich unter Extras => Optionen => Debuggen => Allgemeine Einstellungen das Kontrollkästchen "Verwalteten Kompatibilitätsmodus verwenden" aktivieren.

Im Allgemeinen mache ich so etwas:

public static class FakeHttpContext
{
    public static void SetFakeContext(this Controller controller)
    {

        var httpContext = MakeFakeContext();
        ControllerContext context =
        new ControllerContext(
        new RequestContext(httpContext,
        new RouteData()), controller);
        controller.ControllerContext = context;
    }


    private static HttpContextBase MakeFakeContext()
    {
        var context = new Mock<HttpContextBase>();
        var request = new Mock<HttpRequestBase>();
        var response = new Mock<HttpResponseBase>();
        var session = new Mock<HttpSessionStateBase>();
        var server = new Mock<HttpServerUtilityBase>();
        var user = new Mock<IPrincipal>();
        var identity = new Mock<IIdentity>();

        context.Setup(c=> c.Request).Returns(request.Object);
        context.Setup(c=> c.Response).Returns(response.Object);
        context.Setup(c=> c.Session).Returns(session.Object);
        context.Setup(c=> c.Server).Returns(server.Object);
        context.Setup(c=> c.User).Returns(user.Object);
        user.Setup(c=> c.Identity).Returns(identity.Object);
        identity.Setup(i => i.IsAuthenticated).Returns(true);
        identity.Setup(i => i.Name).Returns("admin");

        return context.Object;
    }


}

Und den Kontext so zu initiieren

FakeHttpContext.SetFakeContext(moController);

Und die Methode direkt im Controller aufrufen

long lReportStatusID = -1;
var result = moController.CancelReport(lReportStatusID);
5
aggaton

Wenn Ihre Anwendung von Drittanbietern intern umgeleitet wird, ist es besser, HttpContext wie folgt zu verspotten:

HttpWorkerRequest initWorkerRequest = new SimpleWorkerRequest("","","","",new StringWriter(CultureInfo.InvariantCulture));
System.Web.HttpContext.Current = new HttpContext(initWorkerRequest);
System.Web.HttpContext.Current.Request.Browser = new HttpBrowserCapabilities();
System.Web.HttpContext.Current.Request.Browser.Capabilities = new Dictionary<string, string> { { "requiresPostRedirectionHandling", "false" } };
4
Divang