为Factory类创建的对象注入Mocks
我有以下课程:
public class MyClass { private Apple apple; public void myMethod() { apple = AppleFactory.createInstance(someStringVariable); .... .... .... } }
和测试类:
@RunWith(MockitoJUnitRunner.class) public class MyClassTest { @InjectMocks MyClass myClass; @Test public void myMethod(){ ... ... ... } }
我如何在MyClass中将Apple实例注入模拟?
您有3种可能性来解决这个问题:
抽象工厂 :使用具体的工厂类,而不是使用静态方法:
public abstract class AppleFactory { public Apple createInstance(final String str); } public class AppleFactoryImpl implements AppleFactory { public Apple createInstance(final String str) { // Implementation } }
在您的测试类中,模拟工厂:
@RunWith(MockitoJUnitRunner.class) public class MyClassTest { @Mock private AppleFactory appleFactoryMock; @Mock private Apple appleMock; @InjectMocks MyClass myClass; @Before public void setup() { when(appleFactoryMock.createInstance(Matchers.anyString()).thenReturn(appleMock); } @Test public void myMethod(){ ... ... ... } }
PowerMock :使用PowerMock创建静态方法的模拟。 看看我对相关问题的回答 ,看看它是如何完成的。
可测试类 :将Apple
创建包装在protected
方法中,并创建一个覆盖它的测试类:
public class MyClass { private Apple apple; public void myMethod() { apple = createApple(); .... .... .... } protected Apple createApple() { return AppleFactory.createInstance(someStringVariable); } } @RunWith(MockitoJUnitRunner.class) public class MyClassTest { @Mock private Apple appleMock; @InjectMocks MyClass myClass; @Test public void myMethod(){ ... ... ... } private class TestableMyClass extends MyClass { @Override public void createApple() { return appleMock; } } }
当然,在您的测试类中,您应该测试TestableMyClass
而不是MyClass
。
我会告诉你我对每种方法的看法:
-
抽象工厂方法是最好的 – 这是一个隐藏实现细节的清晰设计
-
可测试类 – 是第二个选项,需要最少的更改
-
PowerMock
选项是我最不喜欢的 – 您可以忽略并隐藏您的问题,而不是寻求更好的设计。 但这仍然是一个有效的选择。
关于Avi&Ev0oD的第一个答案。 抽象类只能扩展而不能实现。
public abstract class AppleFactory { public abstract Apple createInstance(final String str); } public class AppleFactoryImpl extends AppleFactory { public Apple createInstance(final String str) { // Implementation } }
除了Avi提出的解决方案之外,您还可以选择第四种可能性:
注入工厂:对我来说,当你已经有refacrot代码时,这是最好的选择。 使用此解决方案,您无需更改产品代码,只需更改工厂类和测试。
public class AppleFactory { private static Apple _injectedApple; public static createInstance(String str) { if (_injectedApple != null) { var currentApple = _injectedApple; _injectedApple = null; return currentApple; } //standard implementation } public static setInjectedApple(Apple apple) { _injectedApple = apple; } }
现在您只需使用静态工厂:
@RunWith(MockitoJUnitRunner.class) public class MyClassTest { @Mock private Apple appleMock; @InjectMocks MyClass myClass; @Before public void setup() { AppleFactory.setInjectedApple(appleMock); } @Test public void myMethod(){ ... ... ... } }