Mockito模拟注释多个对象

时间:2018-06-03 18:35:28

标签: java unit-testing mockito

我试图了解mockito,但我遇到了困难,我试图使用@Mock注释模拟多个对象,但它不会嘲笑它们。它只会模拟第一个对象(mockBoard)。 如果我使用mock(Boardfactory.class)自己模拟BoardFactory它会工作。但我不明白为什么它不会在@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 个答案:

答案 0 :(得分:1)

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

此处,mockLevelFactorymockBoardFactory 始终为空,无论是JUnit4,5,@Rule还是@ExtendWith。为什么?因为一旦你的测试类被实例化就会调用该行,很久之前Mockito就有机会运行并将模拟放入带注释的变量中:

  1. 测试类获得实例化
  2. 执行以下行:

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

  3. Mockito创建了以下模拟:

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

  4. (一段时间后)实际测试方法称为

  5. 这就是mockLevelFactorymockBoardFactoryMapParser 总是为空的原因。它们在您的测试中不为空,但在您创建MapParser 为空。

    解决方案?稍后创建MapParser的实例,例如在@BeforeEach方法...

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

    ......或者只是在测试方法的开头(但可以考虑那种糟糕的风格)。

答案 1 :(得分:0)

您确定第一个字段是否已实际初始化?除非您使用MockitoJUnitRunner运行测试,否则您需要手动初始化测试类的带注释字段,例如:

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

作为旁注,即使正确创建了模拟,我也要注意是否将正确初始化字段mapParser。在测试类初始化之后,可能会注入模拟,因此您可能无法在构造函数或字段初始值设定项中使用模拟字段。您可能需要在@BeforeEach方法中初始化该字段,就像您在开始时一样。