C# 如何为单元测试填充HttpWebResponse.GetResponseAsync()?
我被指派为一个我无权修改的应用程序编写单元测试。我想要进行单元测试的方法进行如下调用:C# 如何为单元测试填充HttpWebResponse.GetResponseAsync()?,c#,unit-testing,mocking,httpwebrequest,microsoft-fakes,C#,Unit Testing,Mocking,Httpwebrequest,Microsoft Fakes,我被指派为一个我无权修改的应用程序编写单元测试。我想要进行单元测试的方法进行如下调用: HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse; 在这个项目中,我已经使用微软的Fakes进行了数千次其他测试,所以我想我在这里也会这样做。在我看来,最简单、最干净的解决方案似乎是填充request.GetResponseAsync()方法。然后我可以返回一些假内容,并确保该方法在不发出请求的情况下
HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse;
在这个项目中,我已经使用微软的Fakes进行了数千次其他测试,所以我想我在这里也会这样做。在我看来,最简单、最干净的解决方案似乎是填充request.GetResponseAsync()
方法。然后我可以返回一些假内容,并确保该方法在不发出请求的情况下正确处理它
GetResponseAsync()
返回一个Task
对象。所以我通常会这样做:
using (ShimsContext.Create())
{
System.Net.Fakes.ShimWebRequest.AllInstances.GetResponseAsync = (x) =>
{
return new Task<WebResponse>(() =>
{
HttpWebResponse toRet = new HttpWebResponse();
return toRet;
});
}
}
使用(ShimsContext.Create())
{
System.Net.Fakes.ShimWebRequest.AllInstances.GetResponseAsync=(x)=>
{
返回新任务(()=>
{
HttpWebResponse toRet=新的HttpWebResponse();
返回托雷特;
});
}
}
问题是上面的代码无法编译,因为
'System.Net.HttpWebResponse.HttpWebResponse()'已过时:'此API
支持.NET Framework基础架构,不打算
直接从代码中使用。”
我知道这个类型已经过时了,我们现在应该使用一些不同的东西,但是我尝试测试的遗留代码不允许我这么奢侈。我看了很多关于这个话题的问题,但似乎没有一个能回答这个问题。你的问题与Shim无关。使用反射绕过过时的问题
HttpWebResponse toRet = Activator.CreateInstance<HttpWebResponse>();
HttpWebResponse toRet=Activator.CreateInstance();
我将我的答案分成两部分;第一部分是您正在寻找的解决方案。。。在第二部分中,我将讨论UT环境中的其他选项(因此这个答案将帮助其他人…)
因为您已经使用了MsFakes,所以可以使用它创建一个实例。
下面的代码段是一个示例,显示了初始化和使用ShimHttpWebResponse
的方法:
[Test]
public async Task InitializeShimHttpWebResponse()
{
using (ShimsContext.Create())
{
ShimWebRequest.AllInstances.GetResponseAsync = (x) =>
{
/* you can replace the var with WebResponse if you aren't going to set any behavior */
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>
,)
要使用内部\公共命令,您必须使用反射,但在大多数情况下,反射无法开箱即用,然后您必须违反一些UT规则(小、快等)
以下两种情况下,反射将立即生效:
您不需要在实例上调用任何有问题的属性/方法,SUT(测试中的主题)只需将此实例传递给他的一个依赖项
您将使用更多反射来初始化实例字段
虽然第一个是一个简单的案例(如果这是你的案例,那么你应该使用反射),但第二个在UT环境中是一个不好的实践;您的UTs不会很小/可读/可维护,执行时间会增加,Microsoft可能会进行一些重构,这可能会破坏您的UTs
对于受保护的一个,您应该通过继承(编译与运行时…)调用它
2。继承:
受保护的C'tor还具有过时属性,但是属性设置为false,这允许您继承此类,然后您将能够更改方法/属性的行为;虚拟-重写,非虚拟-仅当您有权访问类成员(或反射)时
此选项的主要缺点是必须生成的代码量和复杂性
3。使用基于代理的框架:
在场景背后,这些工具使用反射基于类创建继承的实例(组合选项1和2)
这些工具有一些内置方法,可以使伪代码更小/可读/可维护
缺点:(我不会指出基于代理的工具的所有缺点…)
您仍然无法更改非虚拟方法的行为
您没有访问实例成员的权限
这两个问题可以通过使用代码编织工具来解决
4。代码编织工具:
这些工具几乎可以让你做任何事情,这就是为什么这些工具在一般情况下是最好的(我可以用这句话总结主要的缺点-强大的力量带来巨大的责任…)。
在你的情况下,他们为你提供了最好的解决方案;
由于HttpWebResponse
有一个非虚拟方法,并且您不想重构代码,因此这是适合您的正确解决方案
但是,Msfakes没有为您提供UT的附加方法/功能(AssertSwascaled
、计数等),因此,除非您打算用其他工具替换该工具,否则在我看来,您应该将其与代理基础工具(免费工具)结合起来。无论我尝试过什么,我都无法生成ShimXXX类。我正在尝试。我通过简单地关闭VS2019,打开,清理项目,重建项目来修复我的问题