Мокито не признает класс

Я пытался использовать Weld-Junit5 с издевательством над классом. Я издеваюсь над классом, потому что хочу знать, как часто он будет называться.

Но каждый раз, когда я пытаюсь использовать Mockito.verify(), этот издевательский класс выдает "NotAMockException".

Отладчик Intellij проверяет поле как: "Mock for MessageService, hashCode: XY"

Я уже пытался добавить свой тестовый класс в WeldInitiator, но он не хочет работать.

"MessageService" - это реальный класс, а не интерфейс (интерфейс также не будет работать)

Документы

@EnableWeld
class GameServiceTest {

  @WeldSetup
  public WeldInitiator weld = WeldInitiator.from(GameService.class, GameCache.class,
                                                 /** Some More **/,
                                                 GameServiceTest.class).build();
  @Produces
  @ApplicationScoped
  public MessageService createMessageServiceMock() {
    return mock(MessageService.class);
  }

  @Inject
  private MessageService messageService;
  @Inject
  private GameService gameService;

  @Test
  void handleRunningGames() {
    this.gameService.handleRunningGames(null, mock(Session.class));
    // This will throw a org.mockito.exceptions.misusing.NotAMockException
    Mockito.verify(messageService, Mockito.times(1)).writeMessage(any(), any());
  }
}

Я ожидаю, что Injected MessageService - это настоящий макет, на котором я могу вызывать каждую функцию Mockito, но, похоже, это не так.

Что-то не так, или как это правильно сделать?

Я думаю, что я только что решил эту проблему:


  private static final MessageService messageService = mock(MessageService.class);

  @Produces
  @ApplicationScoped
  public MessageService createMessageServiceMock() {
    return messageService;
  }

2 ответа

Чтобы дать некоторое представление о том, как это работает, Weld позволяет Mockito создать нужный объект, а затем принимает его как контекстный экземпляр компонента.

Однако в CDI любой бин с нормальной областью действия должен иметь прокси-сервер, который передается вместо этого экземпляра. Так что ваш продюсер на самом деле делает (потому что это @ApplicationScoped) - создать объект, сохранить его в контексте, а затем создать прокси-сервер и вместо него передать его. Прокси-сервер - это другой объект (делегат без состояния), который "знает", как получить ссылку на фактический экземпляр.

Так что происходит, что прокси вводится в поле, и вы проверяете объект прокси с Mockito.verify() вызов. Очевидно, что прокси-сервер не является имитацией и поэтому не работает. Как предложил пользователь @second, Weld предлагает API для развертывания прокси и получения контекстного экземпляра. Я бы сказал, что API не "уродлив", это просто то, что пользователям в основном не нужно заботиться, но иногда вы не можете избежать этого.

Вы можете избежать использования прокси, используя некоторые из псевдоскопов, которые @Dependent или CDI @Singleton, При этом он должен работать так же хорошо, как и для тестов, замена приложения, ограниченного синглтоном, должна работать.

Что касается вашего обходного пути, я не вижу, как это решает что-либо - это в основном тот же производитель, и из-за области действия он будет вызываться только один раз, поэтому статическое поле не будет иметь значения (поскольку будет особый вызов mock творчество). Вы изменили что-то еще, чем это?

Как пользователь JUnit 5 + Weld-JUnit, я использую следующую схему. Причины объяснены в ответе Силиаруса.

import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.Mock;
import ...

@EnableWeld
@ExtendWith(MockitoExtension.class) // (1)
class GameServiceTest {

    @WeldSetup
    public WeldInitiator weld = WeldInitiator.from(GameService.class, GameCache.class,
                            /** Some More **/,
                            GameServiceTest.class).build();
    @Produces
    @ApplicationScoped // (2)
    @Mock              // (3)
    private MessageService messageServiceMock;

//  @Inject            // (4)
//  private MessageService messageService;
    @Inject
    private GameService gameService;

    @Test
    void handleRunningGames() {
        this.gameService.handleRunningGames(null, mock(Session.class));
        // (5)
        Mockito.verify(messageServiceMock, Mockito.times(1)).writeMessage(any(), any());
    }
}

Пояснения:

  1. Я использую расширение Mockito для удобства
  2. Вам не нужно давать ему область применения
  3. @Mock аннотация обрабатывается MockitoExtension, Опять же, это только для удобства, вы можете создать макет самостоятельно в методе продюсера.
  4. Вам НЕ нужно вводить фиктивный сервис; у тебя есть messageServiceMock! Внедренная вещь здесь будет прокси Weld, как объяснил Siliarus.

    Это забавно описать эту инъекцию немного больше: если боб @ApplicationScopedт. е. "с нормальной областью действия", CDI должен вводить прокси. Если вы используете этот прокси вместо фактического макета, вы получите исключение. Если вы последуете моему совету из (2) и пропустите @ApplicationScopedбин будет зависеть от области видимости, а макет вводится напрямую. В этом случае вы можете использовать введенное поле, но зачем?

  5. Используйте макет напрямую.
Другие вопросы по тегам