roman_hrytsyshyn roman_hrytsyshyn - 18 days ago 5
Java Question

Mockito: Override mock values in different methods

@RunWith(MockitoJUnitRunner.class)
public class Test {

@Mock
private SomeDependency<T> obj;

@InjectMocks
private SomeClass mainObj;

@Test
public void dependencyShouldBeNotNull() {
//here I need one value of SomeDependency obj
assertEquals(2, mainObj.method())
}

@Test
public void dependencyShouldBeNull() {
//here I need SomeDependency obj to be null
assertEquals(1, mainObj.method())
}


Main class:

class SomeClass {
private SomeDependency<T> field;

public int method() {
if(field==null)
return 1;
else
return 2;
}
}


And my question: how to override value of mock according to different methods needs?

Edited
In main SomeClass I have code like this:

if (obj != null) {
//perform some actions
}

Answer

The easiest way to do it is with 2 test classes instead of one because when it executes your test methods it is already too late since the mock has already been injected (unless you use refection which should be avoided).

The first test

@RunWith(MockitoJUnitRunner.class)
public class Test1 {
    @Mock
    private SomeDependency<T> obj;

    @InjectMocks
    private SomeClass mainObj;

    @Test
    public void dependencyShouldBeNotNull() {
        //here I need one value of SomeDependency obj
        assertEquals(2, mainObj.method());
    }
}

The second test

@RunWith(MockitoJUnitRunner.class)
public class Test2 {
    @InjectMocks
    private SomeClass mainObj;

    @Test
    public void dependencyShouldBeNull() {
        //here I need SomeDependency obj to be null
        assertEquals(1, mainObj.method());
    }
}

If you want to do it with only one test class, it is still possible but it is more like a hack because you want a conditional injection which is not a conventional approach, so you will need to inject the mocks programmatically with MockitoAnnotations.initMocks(obj).

Instead of injecting the mocks directly into the test class, we need to rely on wrapper classes that will contain or not the field obj, if not present nothing will be injected so it will be null otherwise you will have a mock injected.

public class TestInjectMocks {

    /**
     * Small interface that will be implemented by the wrapper classes
     * only used to get the main class
     */
    public interface TestConfig {
        SomeClass getSomeClass();
    }

    @Test
    public void dependencyShouldBeNotNull() {
        // This class will allow to get an instance of SomeClass 
        // with the field injected
        TestConfig obj = new TestConfig() {
            @Mock
            private SomeDependency<T> obj;
            @InjectMocks
            private SomeClass mainObj;

            @Override
            public SomeClass getSomeClass() {
                return mainObj;
            }
        };
        MockitoAnnotations.initMocks(obj);
        SomeClass mainObj = obj.getSomeClass();
        //here I need one value of SomeDependency obj
        assertEquals(2, mainObj.method());
    }

    @Test
    public void dependencyShouldBeNull() {
        // This class will allow to get an instance of SomeClass 
        // without the field injected
        TestConfig obj = new TestConfig(){
            @InjectMocks
            private SomeClass mainObj;
            @Override
            public SomeClass getSomeClass() {
                return mainObj;
            }
        };
        MockitoAnnotations.initMocks(obj);
        SomeClass mainObj = obj.getSomeClass();
        //here I need SomeDependency obj to be null
        assertEquals(1, mainObj.method());
    }
}

NB: As we call MockitoAnnotations.initMocks(obj) explicitly the annotation @RunWith(MockitoJUnitRunner.class) is not needed anymore.