Разница между шпионом и издевательством с делегацией в Мокито - PullRequest
0 голосов
/ 02 марта 2020

Я пишу интеграционные тесты для приложения, которое получает определенные объекты от Kafka, проверяет их, выполняет различные преобразования и либо сохраняет преобразованные объекты в другую базу данных, либо отправляет ответ с ошибками обратно в Kafka.

В производственной базе данных есть несколько именованных запросов, которые отображаются на соответствующие методы в соответствующем JpaRepository, например:

public interface PositionRepository extends JpaRepository<Position, Long> {

  @Procedure(name = "generatePositionCode")
  void generatePositionCode(@Param("clientCode") Integer clientCode);
//remainder omitted

Я использую инфраструктуру Mockito и базу данных H2 в памяти для интеграционного тестирования. Поскольку тестовая база данных не содержит таких именованных запросов, я хочу заглушить соответствующие методы в хранилище и ничего не делать, когда они вызываются (это нормально для целей тестирования), но продолжать вызывать другие непустые методы хранилища.

Я всегда думал, что это когда вы должны использовать шпиона в Мокито. Однако, когда я определяю шпиона следующим образом:

@Spy
private PositionRespository positionRepository;

public void setUp() {
    doNothing().when(positionRepository).generatePositionCode(anyInt());
}

, мои интеграционные тесты не выполняются с исключением, которое сводится к:

Причина: org.h2.jdb c .JdbcSQLException: база данных "FT" не найдена; SQL оператор: вызов FT.Ftposgn.generatePositionCode (?) [90013-197]

Если, с другой стороны, я определяю макет с делегированием в классе конфигурации следующим образом:

@Primary
@Bean
public PositionRepository mockPositionRepository(PositionRepository positionRepository) {
    return Mockito.mock(PositionRepository.class, 
                        AdditionalAnswers.delegatesTo(positionRepository));
}

и автоматическое подключение хранилища в классе тестирования:

@Autowired
private PositionRepository positionRepository;

public void setUp() {
    doNothing().when(positionRepository).generatePositionCode(anyInt());
}

все тесты зеленого цвета.

Я пытался обернуть голову, почему эти два метода дают другие результаты, но не могли. Кто-нибудь может пролить немного света?

Спасибо.

Ответы [ 2 ]

0 голосов
/ 02 марта 2020

Мне удалось решить проблему.

Первая проблема заключалась в том, что я использовал аннотацию @Spy вместо аннотации @SpyBean, и поэтому Spring не управлял шпионом как бином.

Следующая ловушка заключалась в том, что Spring Data JPA создает реализации репозиториев в качестве прокси-серверов CGLIB с использованием финальных методов, поэтому шпион не может быть создан с использованием только базовой библиотеки Mockito. Вы должны добавить зависимость org.mockito:mockito-inline от пути к классам.

Последнее замечание заключается в том, что Mockito.verify() и другие подобные методы завершатся с ошибкой MockitoException, потому что они будут вызываться для целевого объекта вместо Spy. Чтобы преодолеть это, создайте Spy как

@SpyBean(proxyTargetAware = false)

. В этом случае при вызове verify() будет использоваться прокси-сервер напрямую вместо цели рекомендованного AOP-компонента.

0 голосов
/ 02 марта 2020

В первом случае вы должны использовать @Spy на PositionRepository вместо PersonRepository. Если вы тестируете PositionRepository и хотите смоделировать метод его класса, тогда вы должны сделать Spy на PositionRepository. Если вы тестируете PositionRepository и хотите издеваться над другим классом, таким как PersonRepository, тогда вы должны сделать Mock на PersonRepository Другими словами: шпион - частичное издевательство, Mock - полное издевательство. Если вы делаете Spy для объекта и не предоставляете when (). ThenReturn (), тогда он попытается использовать существующую реализацию.

...