saravana_pc saravana_pc - 1 month ago 15
Java Question

Inject Mocks for objects created by Factory classes

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?

Avi Avi
Answer

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 this 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.