2

I am doing JUnit tests with Mockito on Spring Mvc. The tests are using @InjectMock and @Mock with when(method(..)).thenReturn(X). The issue is how to @Mock methods that are within a @Inject instance?

I have tried creating two instances such as @InjectMocks Foo fooInstance and @Mock Foo fooInstanceMock; My way of thinking is to differentiate from what instance to inject and what to mock. I also tried using Spy with InjectMocks but it returns a exception.

Actual Class Syntax-

class Foo {
    public X(..) {
        ...
        Y(...); // method call to Y
        ...
    }

    public Y(..) {
        ...
    }
}

Test Syntax -

public class FooTest {
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    @InjectMocks
    Foo fooInstance;

    @Mock
    Foo fooInstanceMock;

    @Test
    public void xTest{
        when(fooInstanceMock.Y(..)).thenReturn(true);
        Boolean result = fooInstance.X(25);
        Assert.assertTrue(result == true)
    }
}

I except the output to be true as when then return true but since it thinks it is a injectMock and it goes into the implementation.

Mureinik
  • 297,002
  • 52
  • 306
  • 350
MTQ
  • 33
  • 1
  • 6

1 Answers1

5

@InjectMocks is used to inject mocks you've defined in your test in to a non-mock instance with this annotation.

In your usecase, it looks like you're trying to do something a bit different - you want a real intance of Foo with a real implementation of x, but to mock away the implmentation of y, which x calls. This can be done by partial mocking, or in Mockito's terminology, spying:

public class FooTest{

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    // Default constructor is used to create a real Foo instance.
    // In the test's body, though, we'll override the behavior of SOME of the methods
    @Spy
    Foo fooInstance;

    @Test
    public void xTest {
        doReturn(true).when(fooInstance).y(/* arguments, presumably 25 */);
        Boolean result = fooInstance.x(25);
        Assert.assertTrue(result);
    }
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • 1
    Thanks for introducing me to Spy annotation. The only issue is with the design pattern I am using it does not have any constructors. So the Foo instance does not have an constructor so is never a object. Would there be any other method? When I change to @Spy it returns a nullPointerException - Maybe due to no object being created? – MTQ Sep 04 '19 at 16:57
  • @MTQ If the class has no constructors, it implicitly has a default constructor (i.e., public, with no arguments). Where are you getting the `null`? – Mureinik Sep 04 '19 at 21:51
  • Yes @Spy has fixed this issue. Setting your answer as the correct answer. Thanks – MTQ Sep 19 '19 at 10:28
  • I used: (at)Spy (at)InjectMock MyClass myclass = new MyClass(); I had to provide it instantiated for it to work – Steve T Dec 15 '22 at 22:17