为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

我会告诉你我对每种方法的看法:

  1. 抽象工厂方法是最好的 – 这是一个隐藏实现细节的清晰设计

  2. 可测试类 – 是第二个选项,需要最少的更改

  3. 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(){ ... ... ... } }