前言:我的默认操作模式是使用IoC容器和构造函数注入。这使得使用模拟依赖项进行测试变得微不足道。
我开始开发一个IntelliJ插件,我想利用控制反转。由于这是一个插件,所以不是一个容器的选项(对吗?)所以我想我需要使用服务定位器模式。
如何使用服务定位器模式测试模拟?
我能想到的最好的方法是为我的定位器使用一个接口,使用静态getter在每个服务的默认构造函数中设置它,并有一个setter,这样我就可以设置一个模拟的定位器。它看起来像这样:
public class MyService {
private IServiceLocator locator;
public MyService() {
setLocator(ServiceLocator.locator());
}
public void setLocator(IServiceLocator locator) {
this.locator = locator;
}
}
现在我可以嘲笑IServiceLocator
并在我的测试中将其设置在MyService
上。然后,我可以期待像locator.dependency1()
之类的调用,并使其返回一个模拟的依赖项。
这种方法的主要问题是定位器设置器只支持测试。 有更好的方法吗?
答案 0 :(得分:1)
首先,我建议阅读优秀的"有效地使用遗留代码"它有一堆模式来处理这样的东西。虽然您正在编写新代码,但您仍然受到与遗留代码相同的一些限制。
为此,一种更简单的方法可能是提供第二个受保护的构造函数,该构造函数显式接受您的依赖项,并使no-arg构造函数使用您想要的默认值。如下所示:
public class MyService {
private Dependency1 dep1;
private Dependency2 dep2;
protected MyService(Dependency1 dep1, Dependency2 dep2) {
this.dep1 = dep1;
this.dep2 = dep2;
}
public MyService() {
this(new ConcreteDependency1(), new ConcreteDependency2());
}
}
您还可以添加注释,以明确这是用于测试,即Guava's @VisibleForTesting
答案 1 :(得分:1)
你可以按照你描述的方式做到这一点。我想到了其他选择:
您可以对服务定位器字段使用包私有访问
现在,如果您将测试类放在与服务相同的包中 - 它可以直接操作测试类中的字段 - 因此很容易模拟服务定位器。
您可以使用测试/模拟库的功能来注入服务定位器
例如,Mockito(https://code.google.com/p/mockito/)可以通过两种方式注入私有字段:
Whitebox
类 - 但这不是最好的主意,因为您必须知道要模拟的字段名称(更改字段名称将中断测试),并且您需要执行Withebox.setInternalState
方法manualy @InjectMocks
注释 - 您创建使用@Mock
注释注释的服务定位器字段,使用@InjectMocks
注释的服务字段和mockito将使用服务定位器字段并自动将其注入服务。您可以使用不依赖于容器的依赖注入机制
例如,检查Google Guice库:https://code.google.com/p/google-guice/
您可以使用"手动"依赖注入
DI是模式 - DI容器/框架使这种模式易于实现,但您可以随时使用工厂类来完成(但需要更多时间)。这里有关于DI的很好的讨论,涵盖"手册" DI:https://www.youtube.com/watch?v=RlfLCWKxHJ0
很难说哪种方案最适合您。如果你想坚持服务定位器 - 我建议Mockito。如果你想要DI - 我推荐Google Guice。 Personaly,我认为服务定位器更像是反模式。