1

I'm just puzzled by this (Mockito 1.10):

@Rule
public MockitoRule rule = MockitoJUnit.rule();

@Mock
private Collection<IndexableField> mockedFieldsFromRetrievedDocument;

@Spy
@InjectMocks
private IndexManager injectedSpyIM = new IndexManager();

@Test
public void numberOfLDocsShouldBePrintedOutWithEachHitLine() throws Exception{

    LOGGER.info( String.format( "# A: %d", mockedFieldsFromRetrievedDocument.hashCode() ));
    LOGGER.info( String.format( "# fFRD %s", injectedSpyIM.getFFRD() ));

Naturally enough, there is a method getFFRD in IndexManager which returns the private field

private Collection<IndexableField> fieldsFromRetrievedDocument;

There is also another private field in IndexManager:

private Collection<Closeable> closeableComponents;

The first logged line gives you a valid hashcode.
The last line says

# fFRD null

When I then went and examined the value of closeableComponents I found that its hashcode was indeed that of the injected mock Collection.

I then tried swapping around the positions of the declarations of these fields in IndexManager: no change.

It appears that the @Mock line here is 1) totally ignoring the generic class and 2) latching on to the Collection<Closeable> in preference to the other for reasons I don't understand...

a bit later

Wow, crazy stuff: I just changed the name of the field closeableComponents to xcloseableComponents. Now the mocked field is indeed doing what I want, i.e. mocking the field fieldsFromRetrievedDocument.

My provisional conclusion, naturally enough, is that Mockito uses the first field name of type Collection<anything> it finds ... in alphabetical order! Presumably the same selection process applies to other cases where there is more than one field of the "same" type. Just googled on this without success: does anyone know if this is documented somewhere?

later still

Following Jeff Bowman's advice I changed things like so:

@Mock(name="fieldsFromRetrievedDocument")
private Collection<?> mockedFieldsFromRetrievedDocument;

... this is the exact spelling, with correct case, of the field in the class. But it was still injecting the wrong Collection<?> as the mock. Then...

I changed from Mockito 1.10 to the latest, 2.3.0: problem solved! A cautionary tale, in that the name attribute is fully documented in the Javadoc API of 1.10...!

mike rodent
  • 14,126
  • 11
  • 103
  • 157

1 Answers1

2

@InjectMocks documentation describes the behavior, which may be less documented or deterministic than you'd prefer:

Property setter injection; mocks will first be resolved by type (if a single type match injection will happen regardless of the name), then, if there is several property of the same type, by the match of the property name and the mock name.

Note 1: If you have properties with the same type (or same erasure), it's better to name all @Mock annotated fields with the matching properties, otherwise Mockito might get confused and injection won't happen.

This makes some sense, because the generic type of the field is erased--not readable at runtime--and because Java's reflection methods getDeclaredFields and getDeclaredMethods are returned "not in any particular order". Matching names are preferred, and everything else is undefined behavior that your renaming happens to manipulate to your advantage; don't count on that behavior.

The concept of naming mocks above refers to the use of the name attribute on the @Mock annotation.

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Thanks again. I next tried giving the test class injected mock the same name as the actual field in the CUT. I didn't think this would work... and I was right! But now I know the `@Mock` annotation has an optional "name" attribute I've got something to work on. You might I suppose have thought that, faced with 2 fields of the same type, their actual names could be compared with those of the CUT... since reflection is being used, if `"mockedFoo".containsIgnoringCase( "foo" )` then `mockedFoo` is assumed to be standing in for `foo`, in the absence of any ambiguities. It seems not! – mike rodent Dec 11 '16 at 00:30
  • PS thought #2: getting generics by reflection: some thoughts here: http://stackoverflow.com/questions/1901164/get-type-of-a-generic-parameter-in-java-with-reflection. Couldn't Mockito try sthg along these lines? – mike rodent Dec 11 '16 at 00:39
  • @mike Please read closer about how "it can't be done" is the right answer to _that question_. The closest you can get (which is what most answers refer to) is that for `class B extends A` or `new A() {}` you can reflectively get A and T out of the _type hierarchy_. There is a way to get generic type information [out of the field definition](http://stackoverflow.com/q/1868333/1426891), but bearing type variables in mind you're asking Mockito to get very clever at the expense of test readability and framework clarity. – Jeff Bowman Dec 11 '16 at 01:08
  • Yes, I was thinking of type hierarchy (I think). So such things would cost in terms of test readability and framework clarity? I bow to your far superior understanding in such matters, and assume Mockito presents us with enough tools for what we might reasonably require from a mocking framework... (NB as a complete Newb I remember recently reading doubts being expressed about PowerMock, for example, offering "too much magic"...) – mike rodent Dec 11 '16 at 11:49