C# HttpContextWrapper所有这些……有用吗?

C# HttpContextWrapper所有这些……有用吗?,c#,mocking,abstraction,C#,Mocking,Abstraction,我一直在清理我们的控制器代码,使每个操作都可以测试。一般来说,这并不太难——在我们有机会使用固定对象的情况下,比如说FormsAuthentication,我们通常会在适当的情况下引入某种形式的包装器,并且会很愉快 出于与本次对话无关的原因,当涉及到HttpContext的使用时,我们决定使用新创建的HttpContextWrapper类,而不是发明一些国产的东西。我们引入的一个功能是在HttpContextWrapper中进行交换(比如说,用于单元测试)。这完全是受Oren Eini使用Dat

我一直在清理我们的控制器代码,使每个操作都可以测试。一般来说,这并不太难——在我们有机会使用固定对象的情况下,比如说FormsAuthentication,我们通常会在适当的情况下引入某种形式的包装器,并且会很愉快

出于与本次对话无关的原因,当涉及到HttpContext的使用时,我们决定使用新创建的HttpContextWrapper类,而不是发明一些国产的东西。我们引入的一个功能是在HttpContextWrapper中进行交换(比如说,用于单元测试)。这完全是受Oren Eini使用DateTimes(,我们也使用这种模式)处理单元测试的方式启发的

公共静态类FooHttpContext
{
公共静态函数当前=()
=>新的HttpContextWrapper(HttpContext.Current);
公共静态无效重置()
{
当前=()=>新的HttpContextWrapper(HttpContext.Current);
}
}
没什么特别的花哨。它在我们的控制器代码中工作得很好。当我们开始编写单元测试时,我们遇到了麻烦。我们使用Moq作为我们的模拟框架,但是唉

var context = new Mock<HttpContextWrapper>() 
var context=new Mock()
中断,因为HttpContextWrapper没有无参数的构造函数。作为一个ctor参数,它取什么?HttpContext对象。所以我发现自己陷入了困境

我使用规定的方法来解耦HttpContext——但我不能在中模拟值,因为原始HttpContext对象是密封的,因此很难测试。我可以映射HttpContextBase,这两者都源于——但这并不能真正理解我所追求的。关于HttpContextWrapper,我是不是遗漏了要点

编辑以澄清意图


我们找到了解决这个问题的方法——但我想我们最终要解决的问题是HttpContextWrapper给表带来了什么价值?我毫不怀疑有人在什么地方玩得很开心!有一刻,但我就是想不起来。我在这里看到的大多数帖子都是从可测试性的角度来讨论它的——但我自己的经验让我相信,在这种情况下,它并没有带来多少好处。除非我们做错了。(完全可能)。

您应该使用抽象的
HttpContextBase
,它比
HttpContextWrapper
更容易模拟

public static Func<HttpContextBase> Current = 
    () => new HttpContextWrapper(HttpContext.Current);

这篇博文很好地解释了这一点:

关键是“vintage”HttpContext没有实现HttpContextBase,并且不是虚拟的,因此不能被模仿。HttpContextBase在3.5中作为可模仿的替代方案引入。但是仍然存在一个问题,vintage HttpContext没有实现HttpContextBase


因此,HttpContextWrapper是一个方便的包装类(或“kludge”),它确实实现了HttpContextBase,并且可以在使用IOC注入“真实”HttpContext时使用,通常使用如下工厂方法:
()=>新的HttpContextWrapper(HttpContext.Current)
一个真实世界的示例,而不是测试。
除了嘲笑之外,我还偶然发现了一个特殊的问题,包装器类确实帮助我解决了这个问题。我们在Azure中有一个应用程序,我们只能控制该应用程序。它位于一个反向代理之后,该代理更改传入请求的主机头并以自定义头发送原始主机。应用程序依赖主机头来构建动态链接、验证重定向等。因此,我们需要一种方法来替换在HttpContext.HttpRequests.Url属性中设置的主机。由于我们使用应用程序中的包装器将HttpContext仅公开为HttpContextBaase,因此我们能够创建一个继承HttpContextWrapper并重写请求的类,然后返回从RequestWrapper继承并重写Url属性的对象。因此,最终应用程序将ASP.NET用于上下文的url中的主机替换为反向代理设置的自定义头中的自定义主机。除了手动搜索使用HttpContext.Request.Url的代码并应用修复程序之外,在应用程序内部没有其他方法可以做到这一点。

你完全正确——我们最终做了什么……但我想我更多的问题是关于包装器到底给表带来了什么——让我来编辑一下清晰性。
HttpContextWrapper
不是抽象,而是
HttpContextBase
的具体实现。我猜它的价值在于它隐藏了
HttpContext
的一些静态和内部方法
HttpContextWrapper
通过将调用转发到ASP.NET-vintage
HttpContext
来实现(可模拟的)
HttpContextBase
。要避开
HttpContext
是不可模仿的这一事实是一个难题。我想我只是质疑使用包装器还是仅仅使用基础——我认为它更多的是一种抽象,而不是一种具体的机制,因为它存在于System.Web.Abstractions中。我还是很想听到关于阿哈的消息!包装纸是绝对的银弹的时刻,否则我会把它标记为答案<代码>变量容器=新的UnityContainer();RegisterType(新注入构造函数(HttpContext.Current))
public static Func<HttpContextBase> Current = 
    () => new HttpContextWrapper(HttpContext.Current);
SomeClass.Current = MockHttpContextBase(); // Sorry I don't know the syntax for Moq