使用Moq模拟HttpContext进行单元测试

12

我需要一个用于单元测试的HttpContext模拟对象,但我遇到了困难。

我正在编写一个使用SessionIdManager以编程方式更改sessionId的方法。而SessionIdManager需要HttpContext而不是HttpContextBase。

但是我找不到任何关于如何创建HttpContext模拟对象的示例。所有现有的示例仅适用于创建HttpContextBase。

我尝试过以下方法,但它们都没有奏效。

HttpContext httpContext = Mock<HttpContext>();
HttpContext httpContext = (HttpContext)GetMockHttpContextBase();

public HttpContextBase GetMockHttpContextBase()
{
   var context = new Mock<HttpContextBase>();
   var request = new Mock<HttpRequestBase>();
   var response = new Mock<HttpResponseBase>();
   var session = new Mock<HttpSessionStateBase>();
   var application = new Mock<HttpApplication>();
   var httpContext = new Mock<HttpContext>();
   var server = new Mock<HttpServerUtilityBase>();
   var user = new Mock<IPrincipal>();
   var identity = new Mock<IIdentity>();
   var urlHelper = new Mock<UrlHelper>();
   var routes = new RouteCollection();
   var requestContext = new Mock<RequestContext>();

   requestContext.Setup(x => x.HttpContext).Returns(context.Object);
   context.Setup(ctx => ctx.Request).Returns(request.Object);
   context.Setup(ctx => ctx.Response).Returns(response.Object);
   context.Setup(ctx => ctx.Session).Returns(session.Object);
   application.Setup(x => x.Context).Returns(httpContext.Object);
   context.Setup(ctx => ctx.ApplicationInstance).Returns(application.Object);
   context.Setup(ctx => ctx.Server).Returns(server.Object);
   context.Setup(ctx => ctx.User).Returns(user.Object);
   user.Setup(ctx => ctx.Identity).Returns(identity.Object);
   identity.Setup(id => id.IsAuthenticated).Returns(true);
   identity.Setup(id => id.Name).Returns("test");
   request.Setup(req => req.Url).Returns(new Uri("http://tempuri.org"));
   request.Setup(req => req.RequestContext).Returns(requestContext.Object);
   requestContext.Setup(x => x.RouteData).Returns(new RouteData());
   request.SetupGet(req => req.Headers).Returns(new NameValueCollection());

   return context.Object;
}
有没有办法模拟HttpContext或者使用HttpContextBase代替HttpContext?请各位帮忙。

参见:https://dev59.com/X3E95IYBdhLWcg3wApLl#2497618 - Mark Seemann
但是就像我说的,你的链接都是关于HttpContextBase的,而不是HttpContext。我需要一个HttpContext来执行“new SessionIDManager().GetSessionID(HttpContext)”操作。无论如何,还是谢谢。 - genki98
可能相关:https://dev59.com/C3I-5IYBdhLWcg3wKE-x - Mark Seemann
我之前尝试过你提供的第三个链接,正如你在我的示例代码中看到的那样。但是它没有起作用,因为新的Mock<HttpContext>()会抛出运行时异常,即使没有它,HttpContextBase.ApplicationInstance.Context也会返回null。所以它没有起作用。而且我正在使用vs 2010,所以第四个链接也无法帮助我。虽然感谢你的帮助。 - genki98
你说的"they didn't work"是什么意思?你遇到了错误吗?它找不到你模拟的对象吗?你的测试产生了意外的结果吗? - Chris
显示剩余2条评论
2个回答

15
这是一个常见的问题,问题的根源在于不要模拟你没有编写的类型。不要尝试通过复杂的模拟来复制你没有编写的类的行为(这意味着你猜测它的功能和需要模拟的内容),而是在你的代码和外部对象HttpContext之间引入一个抽象层,并模拟该抽象层。正如模拟对象的先驱之一所说:
“对我来说关键是乔·沃尔内斯提出了一个激进的观点:“不要模仿你没有拥有的类型”。这意味着:停止为了使用封闭的库而费力跳跃,利用测试发现你的对象及其相互通信。”
来源:http://higherorderlogic.com/2004/02/the-big-idea-is-messaging/

好的建议,我可能需要一个不同的方法来处理这个问题。非常感谢你,Mike。 - genki98
1
@percebus 已修复。 - Dan Atkinson

5
如果你一定要尝试模拟HttpContext,那么你需要使用相当多的样板代码来"模拟"它。
Phil Haack在这篇博客文章中详细描述了这种方法:http://haacked.com/archive/2005/06/11/simulating_httpcontext.aspx/ 引用他的话:“正如我之前所述,我希望有一种“轻量级”的方法来测试这个方法。如果HttpContext.Current是HttpContext的有效实例,那么就不会有问题。幸运的是,HttpContext的静态Current属性既可读又可写。所需的只是将该属性设置为正确创建的HttpContext实例。然而,创建该实例并不像我第一次尝试的那样直截了当。我会省略无聊的细节,只展示最终的解决方案。”
他在这里提供了一个模拟器框架:http://haacked.com/archive/2007/06/19/unit-tests-web-code-without-a-web-server-using-httpsimulator.aspx/ 请注意,随着所有这些模拟对象相关逻辑的非易错性和可能会影响您的测试可靠性,您很可能涉足集成测试。

尽管我选择了Mike的回答作为答案,但这更接近我的预期。非常好的详细回答,谢谢Lilshieste。 - genki98

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接