2016-06-21 69 views
1

我已被分配为我没有修改权限的应用程序编写单元测试。我想单元测试的方法进行调用是这样的:如何为单元测试填充HttpWebResponse.GetResponseAsync()?

HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse; 

我已经使用微软正版正货千在这个项目中的其他测试,所以我想我会做同样的在这里。在我看来,最简单,最干净的解决方案就是使用request.GetResponseAsync()方法。然后,我可以返回一些假内容,并确保该方法正确处理它,而无需实际发出请求。

GetResponseAsync()返回一个Task<WebResponse>对象。所以我通常会做这样的事情:

using (ShimsContext.Create()) 
{ 
    System.Net.Fakes.ShimWebRequest.AllInstances.GetResponseAsync = (x) => 
    { 
     return new Task<WebResponse>(() => 
     { 
      HttpWebResponse toRet = new HttpWebResponse(); 
      return toRet; 
     }); 
    } 
} 

的问题是,上面并没有编译,因为

“System.Net.HttpWebResponse.HttpWebResponse()”已过时:“这种API 支持.NET Framework基础结构,并不打算直接从您的代码中使用 '。

我知道这个类型已经过时了,现在我们应该使用一些不同的东西,但是我试图测试的遗留代码并不允许我那么奢侈。我看了很多关于这个话题的问题,但似乎没有人回答这个问题。

回答

7

我会将我的答案分成两部分;第一部分是您正在寻找的解决方案......第二部分我将讨论您在UT环境中的其他选项(因此,此答案将有助于其他人...)

既然你已经使用MsFakes,您可以使用Shims创建一个实例。 以下片段是其示出了一个实例的方式来初始化和使用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> 

只是为了总结本节;国际海事组织,对于一般情况下,使用代码编织工具(MsFakes)是正确的方式来处理这种情况(我在下一节中更详细地解释)

正如我看到你有4个选项来创建一个新的实例的HttpWebResponse

使用反射 - 在这种情况下,一个坏主意(UT ..)

2.传承 - 自定义的模拟...

使用代理b有框架Eg; Moq,Rhinomocks等

4.使用代码编织工具(就像您已经使用过的那样)。 Msfakes,Typemock Isolator等。

还有一个选择:创建一个集成测试,而不是UT ...

1.反思:

HttpWebResponse有3个C'tors;(publicinternalprotected

要使用内部\公共C'tors,您将不得不使用反射,但是在大多数情况下,它不会开箱即用,然后您将不得不违反一些UT规则(小型,快速等) ...)

2例,其中反射会开箱的是:

  1. 你不调用实例任何有问题的属性/方法和SUT(被测的人)通过这个实例来一个他的依赖关系

  2. 您将使用更多反射初始化实例字段。

虽然第一个是一个简单的例子(如果这是你的情况,那么你应该使用反射)第二个是UT的背景下不好的做法;你的UTs不会很小/可读/可维护,执行时间会增加,微软可能会做一些重构,这可能会破坏你的UTs。

对于受保护的应该通过继承来调用它(compile vs. runtime ...)。

2.继承:

的保护C'tor也有过时的属性,但属性IsError设置为false,这可以让你继承这个类,然后你就可以改变方法/属性的行为;虚拟覆盖,非虚拟 - 仅当您访问类成员(或反射)

此选项的主要缺点是您必须生成的代码数量及其复杂性。

3.使用基于代理框架:

现场这些工具使用反射来创建基于类的继承的实例背后(合并选项1和2) 这些工具有一些内置的方法,使你的假代码更小/可读/可维护。

缺点:(我不会指出的基于代理的工具,所有的缺点...)

  1. 你仍然无法改变的非虚拟方法的行为。

  2. 您没有访问实例成员。

这2个可以通过使用代码编织工具来解决。

4。代码编织工具:

这些工具可以让你做几乎任何事情,这就是为什么这些工具对于一般情况最好的原因(我可以总结句子的主要缺点 - 强大的功能带来巨大的责任.. )。 就你而言,它们为您提供最佳解决方案; 既然HttpWebResponse有一个非虚拟方法,你不想重构你的代码,这对你来说是正确的解决方案。

但是Msfakes并没有为UT提供额外的方法/功能(AssertsWasCalled,counting等等),所以除非你打算用其他工具替换这个工具,你应该把它和代理基础工具(免费工具!!!!)

1

您的问题与Shim无关。使用反射来绕过过时的问题。

HttpWebResponse toRet = Activator.CreateInstance<HttpWebResponse>();