ASP.net:依赖于Web服务代理类的单元测试

时间:2009-10-01 19:59:16

标签: c# .net asp.net web-services unit-testing

假设我有类似以下的课程:

public class Test{
        private RemoteDoc.Documentation docService = new RemoteDoc.Documentation();
        public Test(){}
}

因此,这使得单元测试变得困难,因为它依赖于代理类。您可以通过构造函数传递对象,如下所示:

public class Test{
        private RemoteDoc.Documentation docService;
        public Test(RemoteDoc.Documentation serv)
        {
               docService = serv;
        }
}

现在在我的单元测试中,我可以实例化Test类并将模拟对象传入构造函数。但是,此解决方案并不理想,因为现在其他类必须知道RemoteDoc.Documentation代理类并且具有对它的显式引用。什么是这个问题的好方法?

编辑:更清楚的是,RemoteDoc.Documentation是Web引用的代理类。可以想想,如果您使用的是salesforce.com的api,那么您真正拥有的就是wsdl和disco文件。

5 个答案:

答案 0 :(得分:3)

您提出的涉及通过构造函数传递依赖项的解决方案实际上是理想的。它是一种众所周知的依赖注入(DI)模式,称为构造函数注入

起初看起来弱点实际上是一种力量。虽然Test类的每个消费者(在您的示例中)都必须现在提供代理的某些实现(我在此假设代理是接口或抽象基类),但它们可以提供该抽象的任何实现,而不仅仅是您最初的想法。恭喜:you have just opened your class for extensibility

这仍然存在一个问题,即你究竟在哪里确定哪些依赖关系去哪里?您应该在名为组合根的位置在应用程序的根目录中执行此操作。在this SO answer中详细解释了这一点。

您可以使用DI容器自动连接您的依赖项。一些常见的DI容器是:

答案 1 :(得分:2)

我喜欢RichardOD的方法。对单元测试有用的一个改进是使用模拟对象而不是访问真实的Web服务。这意味着您的测试将与任何外部服务分离,并且运行得更快。

如果代码更改为:

,则可以执行此操作
public class Test
{        
     private RemoteDoc.IDocumentation docService;     

     // Constructor providing default for docService
     public Test()
     {
         docService = new RemoteDoc.Documentation();
     }   

     // Constructor for injection
     public Test(RemoteDoc.IDocumentation serv)       
     { 
          docService = serv;        
     }
}

然后使用模拟框架创建模拟文档对象,如:

...并将其传递给Test(RemoteDoc.IDocumentation serv)构造函数。

由于RemoteDoc.Documentation是一个具体类,您可以使用分部类从RemoteDoc.IDocumentation继承:

namespace RemoteDoc
{
    public interface IDocumentation
    {
        // public functions you want to mock go here
        string GetDocumentation();
    }

    public partial class Documentation : IDocumentation {}
}

答案 2 :(得分:0)

我是马克的第二个方法。为完整起见,另一个选项如下:

public class Test
{        
     private RemoteDoc.Documentation docService;     

     // Constructor providing default for docService
     public Test()
     {
         docService = new RemoteDoc.Documentation();
     }   

     // Constructor for injection
     public Test(RemoteDoc.Documentation serv)       
     { 
          docService = serv;        
     }
}

这意味着您有一个默认实现,并且可以选择插入不同的实现。如果您不想使用容器,则非常有用。

我过去曾使用过这两种方法。在开发非平凡的软件时,DI容器方法通常是更好的方法。

答案 3 :(得分:0)

请注意,使用WCF会更容易,因为服务合同已经是一个接口。你的模拟类只需要实现接口。

答案 4 :(得分:0)

这种做法怎么样?您将不得不编写更多代码来支持代理类中的功能。但它会为您提供单元测试的灵活性。

public interface IDocumentation
{
    // Add whatever functionality you need from RemoteDoc.Documentation
}

public class RemoteDocumnetation : IDocumentation
{
    private RemoteDoc.Documentation docService = new RemoteDoc.Documentation();

    // Implements IDocumentation 
}

public class Test{
        private IDocumentation doc;
        public Test(IDocumentation serv)
        {
               doc= serv;
        }
}