Search code examples
javadependency-injectionmockitoguice

Using Guice, how do I inject a mock object from my unit test, into the class being tested


Consider the following code:

@Singleton
public class MyServiceImpl {
    public int doSomething() {
        return 5;
    }
}

@ImplementedBy(MyServiceImpl.class)
public interface MyService {
    public int doSomething();
}

public class MyCommand {
    @Inject private MyService service;

    public boolean executeSomething() {
        return service.doSomething() > 0;
    }
}

public class MyCommandTest {
    @InjectMocks MyServiceImpl serviceMock;
    private MyCommand command;

    @Before public void beforeEach() {
        MockitoAnnotations.initMocks(this);
        command = new MyCommand();
        when(serviceMock.doSomething()).thenReturn(-1); // <- Error here
    }

    @Test public void mockInjected() {
        boolean result = command.executeSomething();
        verify(serviceMock).doSomething();
        assertThat(result, equalTo(false));
    }
}

My test is falling over when I attempt to stub the doSomething() method on my mock implementation object. I get the error:

org.mockito.exceptions.misusing.MissingMethodInvocationException: when() requires an argument which has to be 'a method call on a mock'. For example: when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because: 1. you stub either of: final/private/equals()/hashCode() methods. Those methods cannot be stubbed/verified. Mocking methods declared on non-public parent classes is not supported. 2. inside when() you don't call method on mock but on some other object.

I am new to dependency injection via Guice, and am not sure why I cannot mock the implementation object in this way?


Solution

  • Test without CDI

    A simple solution is to combine CDI with Constructor Injection, and forget about Guice for the test:

    public class MyCommand {
        private final MyService service;
    
        @Inject
        public MyCommand(MyService service) {
            this.service = service;
        }
    
        public boolean executeSomething() {
            return service.doSomething() > 0;
        }
    }
    
    @RunWith(MockitoJUnitRunner.class)
    public class MyCommandTest {
        @Mock
        MyServiceImpl serviceMock;
        private MyCommand command;
    
        @Before public void beforeEach() {
            MockitoAnnotations.initMocks(this);
            when(serviceMock.doSomething()).thenReturn(-1); // <- Error here
    
            // inject without Guice
            command = new MyCommand(serviceMock);
        }
    }
    

    Test with Mockito doing CDI

    Else, if you do not like Constructor Injection, the test code should look like this:

    @RunWith(MockitoJUnitRunner.class)
    public class MyCommandTest {
        @Mock
        MyServiceImpl serviceMock;
        @InjectMocks 
        private MyCommand command;
    
        private AutoCloseable mockHandler;
    
        @Before
        public void beforeEach() {
            // initialize members annotated with @Mock and @InjectMocks
            mockHandler = MockitoAnnotations.openMocks(this);
    
            when(serviceMock.doSomething()).thenReturn(-1); // <- Error here
        }
    
        @After
        public void afterEach() throws Exception {
            mockHandler.close();
        }
    }