asp.net-mvc 使用 Moq 模拟 HttpContextBase

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1228179/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-07 23:45:50  来源:igfitidea点击:

Mocking HttpContextBase with Moq

asp.net-mvcmoq

提问by Bob Yexley

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:

我有一个单元测试装置,我试图在其中测试 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?
}

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.

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

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?

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

Thanks.

谢谢。

回答by Kurt Schindler

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

我正在使用 Steve Sanderson 在他的Pro Asp.NET MVC 书中包含的一些代码的一个版本......我目前在道德上陷入困境,是否可以在这里发布代码。我如何妥协一个高度精简的版本?;)

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

所以这可以很容易地重用,创建一个类似于下面的类,你将传递你的控制器。这将设置您的模拟并将它们设置为您的控制器的 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:

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

[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!

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

回答by Craig Stuntz

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.

这是Scott Hanselman 发布的 MvcMockHelpers 库的增强版本。这是 Moq 2.0 代码;3 中的语法略有不同。