0

I'm trying to understand mockito but I'm getting stuck, im trying to mock multiple objects using the @Mock annotation, but it will not mock them. It will only mock the first object (mockBoard). If I mock the BoardFactory myself using mock(Boardfactory.class) it will work. But i dont understand why it wont work in the @Mock?

@ExtendWith(MockitoExtension.class)
class MapParserTest {

//mocks just fine
@Mock private Board mockBoard;

//wont mock both factories, sets them to null
@Mock private BoardFactory mockBoardFactory;
@Mock private LevelFactory mockLevelFactory;

//this will work
//private BoardFactory mockBoardFactory = mock(BoardFactory.class);

private MapParser mapParser = new MapParser(mockLevelFactory, mockBoardFactory);
private List<Ghost> ghosts = new ArrayList<>();
private List<Square> startPositions = new ArrayList<>();

@Test
void testParseCharMatrix() {
    //Arrange
    char[][] mapMatrix = new char[1][];
    mapMatrix[0] = new char[]{'#'};

    //nullPointer exception thrown here
    when(mockBoardFactory.createBoard(any(Square[][].class))).thenReturn(mockBoard);

    //Act
    mapParser.parseMap(mapMatrix);

    //Assert
    verify(mockLevelFactory).createLevel(mockBoard, ghosts, startPositions);
}}
2
  • 1
    @Mock does mock the annotated field. Post a complete minimal example reproducing the problem. Mockign a List doesn't make much sense: why not use a real List? It's trivial to create one, isn't it? And why do you moc mapParser, and then create a non-mock one? That doesn't make sense either. Commented Jun 3, 2018 at 18:39
  • @BeforeEach seems to imply that you are using JUnit 5, but that version does not use @Rule anymore. If you are using JUnit 5, you would annotate your class with @ExtendWith(MockitoExtension.class). Rules are a part of JUnit 4. Of course, that doesn't explain why the first @Mock works. Commented Jun 4, 2018 at 5:59

2 Answers 2

2
private MapParser mapParser = new MapParser(mockLevelFactory, mockBoardFactory);

Here, mockLevelFactory and mockBoardFactory will always be null, no matter if JUnit4, 5, @Rule or @ExtendWith. Why? Because that line gets called as soon as your test class is instantiated, long before Mockito had a chance to run and put mocks into your annotated variables:

  1. Test-Class gets instantiated
  2. The following lines get executed:

    private MapParser mapParser = new MapParser(mockLevelFactory, mockBoardFactory);
    private List<Ghost> ghosts = new ArrayList<>();
    private List<Square> startPositions = new ArrayList<>();

  3. Mockito creates the following mocks:

    @Mock private Board mockBoard;
    @Mock private BoardFactory mockBoardFactory;
    @Mock private LevelFactory mockLevelFactory;

  4. (some time later) The actual test method is called

This is why mockLevelFactory and mockBoardFactory are always null in your MapParser. They are not null in your test, but they are null at the moment when you create your MapParser.

The solution? Create the instance of your MapParser later, for example in a @BeforeEach method...

@BeforeEach
public void beforeEach() {
   // This method gets called AFTER Mockito created the mocks
   mapParser = new MapParser(mockLevelFactory, mockBoardFactory);
}

...or also simply at the start of your test method (but one could consider that bad style).

Sign up to request clarification or add additional context in comments.

1 Comment

If using JUnit 4, couldn't you also add a class variable @InjectMocks private MapParser mapParser; which would inject the dependencies? Then you wouldn't need to have that method at all.
0

Are you sure the first field is actually initialized? Unless you run the test with MockitoJUnitRunner, you need to initialize the annotated fields of the test class manually, for example like this:

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

As a side note, even if the mocks are created correctly, then I'm note sure if the field mapParser will be initialized correctly. It's likely that mocks are injected after the test class has been initialized, so you probably can't use mocked field in the constructor or in field initializers. You probably need to initialize that field in a @BeforeEach method, like you had in the beginning.

1 Comment

a) This seems to be JUnit 45, so no Runner. b) Before one of his changes he had the MockitoRule there that should take care of stuff.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.