使用 Moq 模拟 HttpContextBase

发布于 2024-07-29 21:50:00 字数 2005 浏览 4 评论 0原文

我有一个单元测试装置,我试图在 ASP.NET MVC 控制器上测试 ControllerAction,该控制器用于 Web 应用程序上的成员资格功能。 我正在尝试模拟 HttpContext 进行测试。 测试中的 ControllerAction 实际上在 HttpContext 上设置属性,例如 Session 值、Response.Cookies 值等。这不是全部代码,但这里是我尝试运行的测试的粗略示例:

[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?
}

不确定我做错了什么,但如果有人能指出我正确的方向并向我展示如何设置这个模拟 HttpContextBase 对象,以便我的控制器实际上可以在其属性上设置值,我会很高兴对这些属性进行断言,以确保我的 ControllerAction 正在执行我需要的操作。

我是否以错误的方式处理这个问题? 我知道 MVC 控制器有一个 ControllerContext,我可以用它来设置 Session 等的值,但我无法弄清楚如何在不注入它的情况下模拟类似的东西。 有什么方法可以做到这一点吗? (我还需要能够将上下文传递给我的 MembershipProvider)这是更好的方法吗?

谢谢。

I have a unit test fixture in which I'm trying to test a ControllerAction on an ASP.NET MVC controller that's used for membership functions on a web app. I'm trying to mock the HttpContext for the tests. The ControllerAction under test actually sets properties on the HttpContext, such as Session values, Response.Cookies values, etc. This isn't all of the code, but here is a rough sample of the test that I'm trying to get to run:

[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?
}

Not sure what I'm doing wrong, but I'd love it if someone could point me in the right direction and show me how to setup this mock HttpContextBase object such that my controller can actually set values on its properties, and I can make assertions on those properties to ensure that my ControllerAction is doing what I need it to.

Am I approaching this the wrong way? I know that MVC Controllers have a ControllerContext that I can use to set values for Session, etc, but I can't figure out how something like that could be mocked without injecting it. Is there some way of doing that instead? (I also need to be able to pass the context in to my MembershipProvider too) Would that be a better approach?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

生来就爱笑 2024-08-05 21:50:03

我是这样做的

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

这是 Scott Hanselman 发布的 MvcMockHelpers 库的增强版本。 这是最小起订量 2.0 代码; 3. 语法略有不同。

Here's how I do it.

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

This is an enhanced version of the MvcMockHelpers library released by Scott Hanselman. This is Moq 2.0 code; the syntax is slightly different in 3.

筱武穆 2024-08-05 21:50:02

我正在使用 Steve Sanderson 的 Pro Asp.NET MVC 书中包含的一些代码的版本< /a>...我目前面临道德困境,是否可以在此处发布代码。 我是否可以妥协于高度精简的版本? ;)

因此这可以很容易地重用,创建一个类似于下面的类,您将传递给您的控制器。 这将设置您的模拟并将它们设置为控制器的 ControllerContext

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

然后在您的测试方法中,您只需创建 ContextMocks 的实例并传入您正在测试的控制器对象:

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

看起来与 Craig 的示例非常相似,但这是与起订量 v3。 我必须为此向 Steve Sanderson 表示支持 - 我使用它作为测试各种传统上难以测试的东西的基础:cookie、会话、请求方法、查询字符串等等!

I'm using a version of some code Steve Sanderson included in his Pro Asp.NET MVC book... and I'm currently having a moral dilemma whether it's okay to post the code here. How about I compromise with a highly stripped down version? ;)

So this can easily be reused, create a class similar to the one below that you will pass your controller. This will set up your mocks and set them to your controller's ControllerContext

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

And then in your test method you'd just create an instance of ContextMocks and pass in the controller object you're testing:

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

Seems very similar to Craig's examples, but this is with Moq v3. I have to give props to Steve Sanderson for this - I'm using this as a basis for testing all kinds of otherwise traditionally hard-to-test stuff: cookies, session, request method, querystring and more!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文