@InjectMocks and Mockito.mock

2,299 views
Skip to first unread message

whermeling

unread,
May 18, 2011, 3:54:54 AM5/18/11
to mockito
From a little experiment i found that the @InjectMocks annotation can
only inject mocks created by the @Mock annotation. However the @Mock
annotation always requires you to define fields, but these are not
always necessary. In some cases you want the change mock's behavior
(using Mockito.when()), but sometimes the mock is just fine and
doesn't need to be altered This will leave you with 'unused' fields in
your test case.

In my experiment i tried having the mock injected by first created it
with Mockito.mock and then calling MockitoAnnotations.initMocks(this),
but this doesn't work. The mocked class will not be injected into
another class(es) annotated with @InjectMocks

@InjectMocks
private AnotherClass anotherClass; // this class is autowired (using
Spring) with SomeClass!!!

@Before
public void initMocks() {
Mockito.mock(SomeClass.class, "someClass"); // on purpose placing
the mock method before initMocks method
MockitoAnnotations.initMocks(this); // initMocks should take care
of injecting the mock created in the line above
}

Another option would require a API change, for instance something
like:

@InjectMocks("someClass")
private AnotherClass anotherClass; // this class is @Autowired with
SomeClass!!!

or:

@InjectMocks(SomeClass.class)
private AnotherClass anotherClass; // this class is @Autowired with
SomeClass!!!

2 questions:
a) Are my findings correct?
b) What do you think about the ideas illustrated by the code snippets?

Brice Dutheil

unread,
May 18, 2011, 6:09:44 AM5/18/11
to moc...@googlegroups.com
Hi,

Yes that is correct, the mockito injection mechanism only look for fields in the test case. It's working at test instance level.

By the way, i find it fairly weird if not wrong to inject unused mocks. It seems to me this is code/test smell.


My comments on the code snippets:

@InjectMocks
private AnotherClass anotherClass; // this class is autowired (using
Spring) with SomeClass!!!

@InjectMocks alone is not aware at all of any Spring stuff or any other injection dependency mechanism. That is why we don't talk about "autowiring".

 
@Before
public void initMocks() {
   Mockito.mock(SomeClass.class, "someClass"); // on purpose placing
the mock method before initMocks method
   MockitoAnnotations.initMocks(this); // initMocks should take care
of injecting the mock created in the line above
}

That don't work in the actual code base, you create a mock and never store the mock instance, besides as mentioned above MockitoAnnotations.initMocks(this) only works on fields of the test instance.
Anyway if the code could somehow "register" the mock, I would still find this approach weird, it's like a phantom and you still have to declare it.

 
Another option would require a API change, for instance something
like:

@InjectMocks("someClass")
private AnotherClass anotherClass; // this class is @Autowired with
SomeClass!!!

or:

@InjectMocks(SomeClass.class)
private AnotherClass anotherClass; // this class is @Autowired with
SomeClass!!!

Interesting, though I'm not sure about such a feature.
And remember this is not "autowiring".
 
2 questions:
a) Are my findings correct?
Yes
 
b) What do you think about the ideas illustrated by the code snippets?
The last snippets seems interesting, but in my opinion having to rely on a unused mocks seems really weird, because if you need it, there is an interaction with it, and it shall appear somewhere. I think this dependency have much more sense to be declared in the class scope and as a field than elsewhere.

Is it so much bothersome ?


It's yet another debate, but if you are interested on mock injection you might like to have a look at the *experiment* of marek there : http://code.google.com/p/mockarro/


Regards,


-- 
Bryce

marekdec

unread,
May 19, 2011, 4:24:29 AM5/19/11
to mockito
Hi Whermeling,

Mockarro, indeed, provides a way to do what you described here,
however it is not its original intention.

This can be achieved with a following snippet.

public class MyTest() {

@Mock
Service userManagedMock;

@Test
public void someTest() {
initMocks(this);

UnitUnderTest uut = new UnitUnderTest();
initSut(uut, annotatedMocks(this));
}
}

public class UnitUnderTest {
@Autowired
Service definedInTheTest;

@Autowired
AnotherService mockarroManaged;
}

---

Mockarro is capable of injecting the client managed mocks (defined in
the test) or mockarro managed mocks - created by the mockarro engine.
It is build on top of Mockito so they can coexist within the same
test.
Though, as I said Mockarro experiment is being conducted to verify if
some other hypothesis is valid or not: u can read more at:
http://code.google.com/p/mockarro/


And apart from what I said above, I agree with Bryce that is seems
sort of weird that you are not interested at all in the indirect input
(or indirect output) provided by your collaborators. [To make it clear
I'd say that 'happy with the default return values' == 'not interested
at all in the return values'].
Probably it is a good moment to stop for a while and double check if
the interaction with these collaborators is really necessary. As it
does not have any impact on the result of your test in any of
scenarios maybe you could get rid of it?

rgds,
Marek

Szczepan Faber

unread,
May 22, 2011, 3:26:55 PM5/22/11
to moc...@googlegroups.com
Hey,

>However the @Mock annotation always requires you to define fields, but these are not always necessary

Hmmm, what do you mean by 'not necessary'? If the collaborator is not
necessary for the execution of the tested logic (SUT) then I think you
should change the SUT so that you don't need to pass that unnecessary
collaborator. If the collaborator is necessary but it's just a
'dummy', e.g. a mock without any interesting behavior then... well it
is still 'necessary' so it should be documented in the test, be it an
'unused' field ;)

Cheers!
Szczepan

> --
> You received this message because you are subscribed to the Google Groups "mockito" group.
> To post to this group, send email to moc...@googlegroups.com.
> To unsubscribe from this group, send email to mockito+u...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/mockito?hl=en.
>
>

--
Szczepan Faber
Principal engineer@gradleware
Lead@mockito

whermeling

unread,
May 23, 2011, 8:40:13 AM5/23/11
to mockito
Hey,

Thanks for all the responses.

It's just a (i guess) rare case where i the class (which is to be
replaced by a mock) has a method which causes errors when used in the
test case (in my cases it tries to access a database which is not
present). By replacing the class with a Mock, the error is solved, but
other then that, i do not need to alter the behavior any further. To
summarize: the behavior is altered enough by injecting the mock.

I guess in almost any other case you want to alter the behavior of the
mock using Mockito.when(,..).
> > For more options, visit this group athttp://groups.google.com/group/mockito?hl=en.

Brice Dutheil

unread,
May 23, 2011, 8:54:36 AM5/23/11
to moc...@googlegroups.com
Hi,

Well then, you should document in your test with proper naming that this mock serves the purpose you mentionned.

Anyway, if you don't own the erroneous dependency, you might want to better isolate the code you own. Mocking types you don't own might lead to bad surprises.


Regards,


-- 
Bryce

whermeling

unread,
May 24, 2011, 7:37:08 AM5/24/11
to mockito
Ok, will do. I could also explicitly include the following code which
illustrates the intention of mocking SomeClass, which is, doing
nothing. This will remove the warning i get about having an unused
private field (the mock) in my test case.

Mockito.doNothing().when(someClass).someMethod();

Thanks again for the comments.

Brice Dutheil

unread,
May 24, 2011, 8:27:32 AM5/24/11
to moc...@googlegroups.com
Hi,

Mockito.doNothing().when(someClass).someMethod();
Yes, nice way to explicitly document this interaction.

As you can have several @Before methods in JUnit4, i would recommand to put it in a dedicated setup method, such as:

@Before public void someClass_shouldnt_do_anything_because____something() {
  Mockito.doNothing().when(someClass).someMethod();
}



Thanks again for the comments.

You're welcome.



Regards,



-- 
Bryce
Reply all
Reply to author
Forward
0 new messages