我一直在完成清理控制器代码的过程,以使每个操作都可测试。一般来说,这并不是太难 - 我们有机会使用固定的对象,比如说FormsAuthentication,我们通常会在适当的时候引入某种形式的包装器,并且是我们的快乐方式。
由于没有特别与此对话密切相关的原因,在处理HttpContext的使用时,我们决定使用新创建的HttpContextWrapper类,而不是发明自己开发的东西。我们介绍的一件事是能够交换HttpContextWrapper(比如说,用于单元测试)。这完全受到Oren Eini使用DateTimes处理单元测试的方式的启发(see article,我们也使用的模式)
public static class FooHttpContext
{
public static Func<HttpContextWrapper> Current = ()
=> new HttpContextWrapper(HttpContext.Current);
public static void Reset()
{
Current = () => new HttpContextWrapper(HttpContext.Current);
}
}
没有什么特别的花哨。它在我们的控制器代码中运行得很好。当我们去编写单元测试时,就会出现这个问题。我们使用Moq作为我们的模拟框架,但是唉
var context = new Mock<HttpContextWrapper>()
因为HttpContextWrapper没有无参数的ctor而中断。作为ctor参数需要什么?一个HttpContext对象。所以我发现自己陷入困境22。
我正在使用规定的方法来解耦HttpContext - 但是我无法模拟一个值,因为原始的HttpContext对象是密封的,因此很难测试。我可以映射HttpContextBase,它们都来自 - 但这并没有真正让我得到我想要的东西。我是否只是忽略了HttpContextWrapper的某个方面?
编辑以澄清意图
我们找到了解决问题的方法 - 但我想我们正在摒弃的最终问题是HttpContextWrapper给桌子带来了什么价值?我不怀疑某个人完全有一个哈哈!它的时刻,但它不会来找我。我在这里看到的大多数帖子都在可测试性方面进行了讨论 - 但是我自己的经验让我相信它并没有带来太大的影响。除非我们做错了。 (完全可能)。
答案 0 :(得分:34)
您应该使用抽象的HttpContextBase
,而不是HttpContextWrapper
更容易模拟。
public static Func<HttpContextBase> Current =
() => new HttpContextWrapper(HttpContext.Current);
在你的单元测试中:
SomeClass.Current = MockHttpContextBase(); // Sorry I don't know the syntax for Moq
答案 1 :(得分:34)
这篇博文很好地解释了它:
http://splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext
关键是'vintage'HttpContext没有实现HttpContextBase,并且不是虚拟的,因此不能被Mocked。 HttpContextBase在3.5中被引入作为可模拟的替代方案。但仍有问题是老式的HttpContext 没有实现 HttpContextBase。
所以HttpContextWrapper是一个方便的包装类(或'kludge'),它实现了HttpContextBase,可以在使用IOC注入'真正的'HttpContext时使用,通常使用这样的工厂方法:() => new HttpContextWrapper(HttpContext.Current)
答案 2 :(得分:0)
除了测试外,还有一个真实的例子。 除了嘲笑,我还偶然发现了包装器类确实帮助我解决的一个特殊问题。我们在Azure中有一个应用程序,并且只能对该应用程序进行控制。它位于反向代理后面,该反向代理更改传入请求的主机标头,并在自定义标头中发送原始主机。该应用程序依赖主机标头来构建动态链接,验证重定向等。因此,我们需要一种替代HttpContext.HttpRequests.Url属性中设置的主机的方法。由于我们在应用程序的各处使用包装器将HttpContext仅公开为HttpContextBaase,因此我们能够创建一个继承HttpContextWrapper并覆盖Request的类,然后返回从RequestWrapper继承并覆盖Url属性的对象。因此,最终应用程序用反向代理设置的自定义标头中的自定义主机替换了ASP.NET用于上下文的URL中的主机。除了手动搜索使用HttpContext.Request.Url的代码并应用修复程序之外,没有其他方法可以从应用程序内执行此操作。