Question

J'ai un appareil de test unitaire dans lequel j'essaie de tester un ControllerAction sur un contrôleur ASP.NET MVC utilisé pour les fonctions d'appartenance sur une application Web. J'essaie de me moquer du HttpContext pour les tests. ControllerAction sous test définit en réalité les propriétés sur le HttpContext, telles que les valeurs de session, les valeurs Response.Cookies, etc. Ce n'est pas tout le code, mais voici un exemple approximatif du test que j'essaie de lancer. :

[Test]
public void ValidRegistrationDataSuccessfullyCreatesAndRegistersUser()
{
    var context = new Mock<HttpContextBase>() {DefaultValue = DefaultValue.Mock};
    context.SetupAllProperties();
    var provider = new Mock<MembershipProvider>(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?
}

Je ne sais pas ce que je fais de mal, mais j'aimerais que quelqu'un me dirige dans la bonne direction et me montre comment configurer cet objet fictif HttpContextBase de sorte que mon contrôleur puisse réellement définir des valeurs pour ses propriétés, et Je peux faire des assertions sur ces propriétés pour m'assurer que mon ControllerAction fait ce dont j'ai besoin.

Est-ce que je l'aborde de la mauvaise façon? Je sais que les contrôleurs MVC ont un ControllerContext que je peux utiliser pour définir des valeurs pour Session, etc., mais je ne peux pas comprendre comment une telle chose pourrait être simulée sans l'injecter. Y a-t-il un moyen de faire cela à la place? (Je dois aussi pouvoir transmettre le contexte à mon fournisseur de services, MembershipProvider également) Est-ce une meilleure approche?

Merci.

Était-ce utile?

La solution

J'utilise une version du code Steve Sanderson inclus dans son Pro Asp.NET. MVC book ... et je suis actuellement confronté à un dilemme moral, à savoir si je peux poster le code ici. Et si je compromettais avec une version très dépouillée? ;)

Pour que cela puisse facilement être réutilisé, créez une classe similaire à celle ci-dessous que vous allez transmettre à votre contrôleur. Cela va configurer vos mocks et les configurer sur ControllerContext de votre contrôleur

public class ContextMocks
{
    public Moq.Mock<HttpContextBase> HttpContext { get; set; }
    public Moq.Mock<HttpRequestBase> Request { get; set; }
    public RouteData RouteData { get; set; }

    public ContextMocks(Controller controller)
    {
        //define context objects
        HttpContext = new Moq.Mock<HttpContextBase>();
        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);
    }
}

Dans votre méthode de test, vous créez simplement une instance de ContextMocks et transmettez l'objet contrôleur que vous testez:

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

Cela ressemble beaucoup aux exemples de Craig, mais c'est avec Moq v3. Je dois donner des accessoires à Steve Sanderson pour cela. Je l'utilise comme base pour tester toutes sortes de choses difficiles à tester: cookies, session, méthode de requête, chaîne de requête et plus encore!

Autres conseils

Voici comment je le fais .

    public static HttpContextBase FakeHttpContext()
    {
        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>();

        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<string>()))
            .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;
    }

Il s'agit d'une version améliorée de la de la bibliothèque MvcMockHelpers publiée par Scott Hanselman . Ceci est le code Moq 2.0; la syntaxe est légèrement différente dans 3.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top