为工厂类创建的对象注入模拟

问题描述 投票:0回答:3

我有以下课程:

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 实例作为模拟?

java spring junit mockito
3个回答
29
投票

您有 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
    选项是我最不喜欢的 - 你没有寻求更好的设计,而是忽略并隐藏了你的问题。但这仍然是一个有效的选择。

0
投票

除了Avi提出的解决方案之外,你还可以选择第四种可能:

注入工厂: 对我来说,当您已经有要重构的代码时,这是最好的选择。使用此解决方案,您不必更改生产代码,而只需更改工厂类和测试。

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

0
投票

第五种方法是从类中提取接口,然后为其生成存根实现。 从这里,您可以模拟存根并使用mockito强大的接口,或者简单地根据您的需求调整存根。

示例

首先解压界面:

interface AppleInterface {
  String foo();
}

class Apple implements AppleInterface {        
  String  foo(){
   ...
  }
}

然后,在你的测试中:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
    //the stub implementation 
    class AppleStub implements AppleInterface  {
     ...
    }
@Test
void testFoo(){
   var mockedApple = mock(AppleStub.class)
   when(mockedApple).foo().thenReturn("Mocked value");
   ...
}
© www.soinside.com 2019 - 2024. All rights reserved.