Java 为工厂类创建的对象注入 Mocks

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21879804/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 11:14:18  来源:igfitidea点击:

Inject Mocks for objects created by Factory classes

javaspringjunitmockito

提问by saravana_pc

I have the following class:

我有以下课程:

public class MyClass {        
    private Apple apple;

    public void myMethod() {
       apple = AppleFactory.createInstance(someStringVariable);
       ....
       ....
       ....
    }
}

And the Test class:

和测试类:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

        @InjectMocks 
        MyClass myClass;

        @Test
        public void myMethod(){
         ...
         ...
         ...
        }
    }

How could I inject an Apple instance as a mock in MyClass?

如何在 MyClass 中注入一个 Apple 实例作为模拟?

采纳答案by Avi

You have 3 possibilities to solve this:

您有 3 种可能性来解决这个问题:

Abstract factory: Instead of using a static method, use a concrete factory class:

抽象工厂:不使用静态方法,而是使用具体工厂类:

public abstract class AppleFactory {
    public Apple createInstance(final String str);
}

public class AppleFactoryImpl implements AppleFactory {
    public Apple createInstance(final String str) { // Implementation }
}

In your test class, mock the factory:

在您的测试类中,模拟工厂:

@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: Use PowerMock to create a mock of a static method. Look at my answer to a relevant questionto see how it's done.

PowerMock:使用 PowerMock 创建静态方法的模拟。查看我对相关问题的回答,了解它是如何完成的。

Testable class: Make the Applecreation wrapped in a protectedmethod and create a test class that overrides it:

可测试类:将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;
       }
    }
}

Of course, in your test class you should test TestableMyClassand not MyClass.

当然,在你的测试类中你应该测试TestableMyClass而不是MyClass.

I'll tell you my opinion on each of the methods:

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

  1. The abstract factory method is the best one- This is a clear design that hides the implementation details

  2. The testable class - Is the second option which requires minimum changes

  3. The PowerMockoption is my least favorite - Instead of going for a better design, you ignore and hide your problem. But that's still a valid option.
  1. 抽象工厂方法是最好的- 这是一个隐藏实现细节的清晰设计

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

  3. 这个PowerMock选项是我最不喜欢的——你没有去寻求更好的设计,而是忽略并隐藏你的问题。但这仍然是一个有效的选择。

回答by Glauco Cucchiar

In addition of the solution proposed by Avi, you can choose a fourth possibility:

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

Inject into Factory: This is, for me, the best option when you already have code to refacrot. With this solution you don't have to change porduction code but only factory class and test.

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

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;
    }
}

Now you can use your static factory simply:

现在你可以简单地使用你的静态工厂:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    private Apple appleMock;

    @InjectMocks 
    MyClass myClass;

    @Before
    public void setup() {
        AppleFactory.setInjectedApple(appleMock);
    }

    @Test
    public void myMethod(){
     ...
     ...
     ...
    }
}

回答by ugurkocak1980

Regarding the first answer of Avi & Ev0oD. Abstract classes can only be extended and not implemented.

关于 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 }
}