With a very simple Mockito run JUnit test and class I am seeing different output when the test is run with Java 1.6.0_32 and Java 1.7.0_04 and want to understand why this is happening. I suspect there is some type erasure going on but would like a definitive answer.

Here is my example code and instructions on how to run from the command line:

FooServiceTest.java

import org.junit.*;
import org.junit.runner.*;
import org.mockito.*;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.*;
import java.util.*;

@RunWith(MockitoJUnitRunner.class)
public class FooServiceTest {
  @Mock Map<String, String> mockStringString;
  @Mock Map<String, Integer> mockStringInteger;

  @InjectMocks FooService fooService;

  public static void main(String[] args) {
    new JUnitCore().run(FooServiceTest.class);
  }

  @Before
  public void setup() {
    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void checkInjection() {
    when(mockStringString.get("foo")).thenReturn("bar");
    fooService.println();
  }
}

FooService.java

import java.util.*;

public class FooService {
  private Map<String, String> stringString = new HashMap<String, String>();
  private Map<String, Integer> stringInteger = new HashMap<String, Integer>();

  public void println() {
    System.out.println(stringString.get("foo") + " " + stringInteger);
  }
}

To compile and run this example:

  • save the above into files
  • download and put in the same directory junit.4.10.jar and mockito-all-1.9.0.jar
  • set PATH to include a JDK
  • compile with javac -cp junit-4.10.jar;mockito-all-1.9.0.jar *.java
  • run with java -cp .;junit-4.10.jar;mockito-all-1.9.0.jar FooServiceTest

I believe the output from above is I null {} because @InjectMocks field injection cannot correctly resolve the types since they are both of type Map. Is this correct?

Now changing one of the mock names to match the field in the class should allow Mockito to find a match. For example changing

@Mock Map<String, Integer> mockStringInteger;

to

@Mock Map<String, Integer> stringInteger;

then compiling/running with Java 1.6.0_32 gives (IMHO the expected) output bar stringInteger but with 1.7.0_04 gives null stringInteger.

Here is how I am running it (from a command line in Windows 7):

E:\src\mockito-test>set PATH="C:\Program Files (x86)\Java\jdk1.6.0_32\bin"
E:\src\mockito-test>javac -cp junit-4.10.jar;mockito-all-1.9.0.jar *.java
E:\src\mockito-test>java -cp .;junit-4.10.jar;mockito-all-1.9.0.jar FooServiceTest
    bar stringInteger
E:\src\mockito-test>set PATH="C:\Program Files (x86)\Java\jdk1.7.0_04\bin"
E:\src\mockito-test>javac -cp junit-4.10.jar;mockito-all-1.9.0.jar *.java
E:\src\mockito-test>java -cp .;junit-4.10.jar;mockito-all-1.9.0.jar FooServiceTest
    null stringInteger
link|improve this question

feedback

1 Answer

Is this correct?

Indeed, due to type erasure, Mockito can't see the difference between the various Maps at run time / via reflection, which is giving Mockito a hard time doing the proper injection.

The null response to stringString.get("foo") can have two causes:

  1. stringString properly mocked, but stubbing did not take place (get always return null).
  2. stringString not mocked, so still a HashMap without a value for "foo", so get will return null.

The {} response to the stringInteger variable means it has been initialized (in the class) with an actual (empty) HashMap.

So what your output tells you is that stringInteger is not mocked. What about stringString?

If none of the two @Mocks have names that match any of the fields of the class under test, nothing is mocked. The reason? I suspect it can't decide which field to inject into, so it doesn't do any mocking. You can verify this by displaying both variables, which will both yield {}. This explains your null value.

If one of the @Mocks has a name that matches, and the other has one that doesn't (your modification, one mockedStringString and one stringInteger, i.e., having the same name), what should Mockito do?

What you want it to do is only inject one of them and only in the field with the corresponding name. In your case, you have mockedStringString (you would expect this not to match) and stringInteger (you would expect it to match). Since you stubbed the mockedStringString (!), which won't match, the expected outcome would be null.

In other words, I think the Java 7 response is OK, and the Java 6 one is not OK, for the specific example given.

To see what is going on for the (unexpected) behavior you get for Java 6, try having just one @Mock -- if I properly mock stringString and have no mock for stringInteger, the mock for for stringString is injected into the stringInteger field. In other words, Mockito seems to first figure out that it can inject (given the name), and then injects the mock to one of the matching possibilities (but not necessarily the right one).

link|improve this answer
feedback

Your Answer

 
or
required, but never shown

Not the answer you're looking for? Browse other questions tagged or ask your own question.