我会把回答分成两个部分;第一部分是你正在寻找的解决方案......在第二部分中,我将讨论在UT上下文中您拥有的其他选项(因此此答案将帮助其他人...)。
由于您已经使用了MsFakes,因此可以使用Shims创建实例。
以下代码片段是一个示例,展示了初始化和使用
ShimHttpWebResponse
的方式:
[Test]
public async Task InitializeShimHttpWebResponse()
{
using (ShimsContext.Create())
{
ShimWebRequest.AllInstances.GetResponseAsync = (x) =>
{
var res = new ShimHttpWebResponse();
return Task.FromResult((WebResponse)res);
};
ShimWebRequest.CreateString = uri =>
{
WebRequest req = new ShimFileWebRequest();
return req;
};
var request = WebRequest.Create("");
var response = await request.GetResponseAsync() as HttpWebResponse;
Assert.IsNotNull(response);
}
}
伪造配置:
<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
<Assembly Name="System" Version="4.0.0.0"/>
<ShimGeneration>
<Add FullName="System.Net.HttpWebResponse"/>
<Add FullName="System.Net.WebRequest"/>
<Add FullName="System.Net.HttpWebRequest"/>
<Add FullName="System.Net.FileWebRequest"/>
</ShimGeneration>
</Fakes>
简单总结一下这部分内容:在我看来,对于通用情况,使用代码织入工具(MsFakes)是处理这种情况的正确方法。(我在下一节中会更详细地解释)
我认为你有四种选项来创建HttpWebResponse
的新实例:
1. 使用反射-在这种情况下不是一个好主意(UT..)
2. 继承-自定义模拟...
3. 使用基于代理的框架,例如Moq、Rhinomocks等。
4. 使用代码织入工具(就像你已经使用过的...)例如Msfakes、Typemock Isolator等。
还有一种选择:创建一个集成测试而不是UT...
1. 反射:
HttpWebResponse
有3个构造函数;(公共, 内部, 受保护)
要使用内部\公共构造函数,您将需要使用反射,但在大多数情况下,它不会直接奏效,然后您将不得不违反一些UT规则(小型、快速等等...)
反射将直接奏效的两种情况是:
您没有在实例上调用任何有问题的属性/方法,并且SUT(测试对象)只是将此实例传递给他的某个依赖项
您将使用更多的反射来初始化实例字段。
虽然第一个是一个简单的情况(如果这是您的情况,那么您应该使用反射),但第二个是UT上下文中的不良实践;您的UTs将不会是小型/可读/可维护的,执行时间将增加,Microsoft可能会进行一些重构,这可能会破坏您的UTs。
对于受保护的构造函数,您应该通过继承调用它(编译时与运行时...)。
2. 继承:
受保护的 C'tor 也有过时属性,但属性
IsError 被设置为 false,这允许您继承此类,然后您将能够更改方法/属性的行为; 虚拟 - 覆盖,非虚拟 - 只有在您访问类成员(或反射)时才能使用。
这种选择的主要缺点是您需要生成的代码量及其复杂性。
3. 使用基于代理的框架:
在幕后,这些工具使用反射基于类创建一个继承实例(结合选项 1 和 2)。这些工具具有一些内置方法,可使您的虚假代码变得更小/可读/易于维护。
缺点:(我不会指出所有基于代理的工具的缺点...)
1. 您仍然无法更改非虚拟方法的行为。
2. 您无法访问实例成员。
这两个问题可以通过使用代码编织工具来解决。
4. 代码编织工具:
这些工具可以让你做几乎任何事情,这也是为什么这些工具在通用情况下最好的原因(我可以用一句话概括主要缺点——伴随着强大的力量而来的是巨大的责任...)。对于你的情况,它们提供了最佳解决方案;由于 HttpWebResponse
具有非虚拟方法且你不想重构代码,因此这是你的正确解决方案。
然而,Msfakes 不提供额外的 UT 方法/功能(例如:AssertsWasCalled
、计数等),因此,除非你打算使用其他工具替换该工具,否则我认为你应该将其与代理基础工具结合使用(免费工具!!!)。