web-dev-qa-db-fra.com

Mock HttpContext.Current dans la méthode Test Init

J'essaie d'ajouter des tests unitaires à une application ASP.NET MVC que j'ai créée. Dans mes tests unitaires, j'utilise le code suivant:

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

    ...
}

Avec les aides suivantes pour simuler le contexte du contrôleur:

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

Cette classe de test hérite d'une classe de base qui présente les caractéristiques suivantes:

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

Dans cette méthode, il appelle une bibliothèque (sur laquelle je n’ai aucun contrôle) qui essaie d’exécuter le code suivant:

HttpContext.Current.User.Identity.IsAuthenticated

Maintenant, vous pouvez probablement voir le problème. J'ai défini le faux HttpContext sur le contrôleur mais pas dans cette méthode Init de base. Les tests unitaires/moqueurs sont très nouveaux pour moi, donc je veux m'assurer de bien faire les choses. Quelle est la bonne façon pour moi de simuler le HttpContext afin qu'il soit partagé entre mon contrôleur et les bibliothèques appelées dans ma méthode Init.

166
nfplee

_HttpContext.Current_ renvoie une instance de System.Web.HttpContext , qui ne s'étend pas System.Web.HttpContextBase . HttpContextBase a été ajouté plus tard pour que HttpContext soit difficile à simuler. Les deux classes sont fondamentalement non liées ( HttpContextWrapper est utilisé comme adaptateur entre elles).

Heureusement, HttpContext lui-même est suffisamment falsifiable pour que vous remplaciez IPrincipal (Utilisateur) et IIdentity.

Le code suivant s'exécute comme prévu, même dans une application console:

_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

Under Test Init fera également le travail.

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

Je sais que c'est un sujet plus ancien, mais nous faisons très régulièrement des simulations d'applications MVC pour des tests unitaires.

Je voulais juste ajouter mon expérience Mocking d'une application MVC 3 utilisant Moq 4 après la mise à niveau vers Visual Studio 2013. Aucun des tests unitaires ne fonctionnait en mode débogage et le HttpContext indiquait "ne pouvait pas évaluer l'expression" lors d'une tentative d'aperçu des variables. .

Il s'avère que Visual Studio 2013 a des problèmes pour évaluer certains objets. Pour que le débogage des applications Web simulées fonctionne à nouveau, je devais cocher la case "Utiliser le mode de compatibilité gérée" dans Outils => Options => Débogage => Paramètres généraux.

Je fais généralement quelque chose comme ça:

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


}

Et initier le contexte comme ça

FakeHttpContext.SetFakeContext(moController);

Et en appelant directement la méthode dans le contrôleur

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

Si votre application tierce redirige en interne, il est donc préférable de simuler HttpContext de la manière suivante:

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