Inject Mocks for objects created by Factory classes

隐身守侯 提交于 2019-12-18 18:54:37

问题


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?


回答1:


You have 3 possibilities to solve this:

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 question to see how it's done.

Testable class: Make the Apple creation wrapped in a protected method and create a test class that overrides it:

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 TestableMyClass and not 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 PowerMock option is my least favorite - Instead of going for a better design, you ignore and hide your problem. But that's still a valid option.



回答2:


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

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

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



回答3:


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

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


来源:https://stackoverflow.com/questions/21879804/inject-mocks-for-objects-created-by-factory-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!