假设我有类似以下的课程:
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文件。
答案 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;
}
}