Mockito mock annotation для нескольких объектов

Я пытаюсь понять mockito, но я застреваю, я пытаюсь издеваться над несколькими объектами с помощью аннотации @Mock, но он не будет издеваться над ними. Он будет издеваться только над первым объектом (mockBoard). Если я сам издеваюсь над BoardFactory, используя mock (Boardfactory.class), он будет работать. Но я не понимаю, почему это не работает в @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);
}}
@Mock имитирует аннотированное поле. Опубликуйте полный минимальный пример, воспроизводящий проблему. Имитация списка не имеет особого смысла: почему бы не использовать настоящий список? Создать такой - нетрудно, не правда ли? И почему вы делаете moc mapParser, а потом создаете немодельный? Это тоже не имеет смысла.
JB Nizet 03.06.2018 20:39
@BeforeEach, похоже, подразумевает, что вы используете JUnit 5, но эта версия больше не использует @Rule. Если вы используете JUnit 5, вы должны аннотировать свой класс с помощью @ExtendWith(MockitoExtension.class). Правила являются частью JUnit 4. Конечно, это не объясняет, почему работает первый @Mock.
Florian Schaetz 04.06.2018 07:59
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
2 191
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы уверены, что первое поле действительно инициализировано? Если вы не запускаете тест с MockitoJUnitRunner, вам необходимо вручную инициализировать аннотированные поля тестового класса, например, следующим образом:

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

В качестве примечания, даже если макеты созданы правильно, я уверен, что поле mapParser будет правильно инициализировано. Вероятно, что макеты внедряются после инициализации тестового класса, поэтому вы, вероятно, не сможете использовать макет поля в конструкторе или в инициализаторах полей. Вам, вероятно, потребуется инициализировать это поле в методе @BeforeEach, как в начале.

а) Кажется, это JUnit 45, поэтому нет Runner. б) Перед одним из его изменений у него было MockitoRule, которое должно обо всем позаботиться.

Florian Schaetz 04.06.2018 20:19
Ответ принят как подходящий
private MapParser mapParser = new MapParser(mockLevelFactory, mockBoardFactory);

Здесь mockLevelFactory и mockBoardFactory будут всегда равны нулю, независимо от того, JUnit4, 5, @Rule или @ExtendWith. Почему? Потому что эта строка вызывается, как только создается ваш тестовый класс, задолго до того, как у Mockito появилась возможность запустить и поместить макеты в ваши аннотированные переменные:

  1. Создается экземпляр Test-Class
  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. (некоторое время спустя) Фактический метод тестирования называется

Вот почему mockLevelFactory и mockBoardFactory равны нулю всегда в вашем MapParser. Они не равны нулю в вашем тесте, но они находятся равны нулю в тот момент, когда вы создаете свой MapParser.

Решение? Создайте экземпляр вашего MapParser позже, например, с помощью метода @BeforeEach ...

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

... или просто в начале вашего метода тестирования (но это можно считать плохим стилем).

Если вы используете JUnit 4, не могли бы вы также добавить переменную класса @InjectMocks private MapParser mapParser;, которая будет вводить зависимости? Тогда вам вообще не понадобится этот метод.

Shaun McCready 16.07.2020 02:02

Другие вопросы по теме