@MockBean, кажется, перезапускает создание контекста и завершается неудачно послеMigrate.sql - PullRequest
1 голос
/ 28 мая 2019

У меня есть два класса интеграционных испытаний.Один из этих классов зависит от bean-компонента, который обращается к внешнему сервису, поэтому мне нужно смоделировать этот bean-компонент, и @MockBean кажется идеальным для этого.Для введения некоторых семян в БД я использую flyway afterMigrate.sql.Так что вот в горячем виде это выглядит так:

@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest
@Transactional
@Rollback
class FooTest {

  @Autowired
  private MyService myService;
}

@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest
@Transactional
@Rollback
class BarTest {

  @MockBean
  private ExternalService;

  @Autowired
  private MyService myService;
}

И afterMigrate.sql:

INSERT INTO my_table (id, name) VALUES (1, 'John Doe')

Проблема появилась, когда я аннотировал ExternatService как @MockBean, как сейчас afretMigrate.sql запускается дважды, и я получаю сообщение об ошибке:

java.lang.IllegalStateException: Failed to load ApplicationContext
....
Message    : ERROR: duplicate key value violates unique constraint "my_table_pkey"

Когда я изменяю @MockBean на @Autowired, ошибка исчезает, и контекст создается без проблем.Кроме того, тесты запускаются без проблем, если я запускаю BarTest отдельно.Это не ожидаемое поведение для @MockBean, как сказано в документации:

Любой существующий одиночный бин того же типа, определенного в контексте, будет заменен на макет.Если существующий бин не определен, будет добавлен новый.Зависимости, которые известны контексту приложения, но не являются компонентами (например, зарегистрированными напрямую), не будут найдены, и фиктивный компонент будет добавлен в контекст вместе с существующей зависимостью.

Это нескажем, что контекст будет воссоздан.

Ответы [ 2 ]

4 голосов
/ 28 мая 2019

Потому что, когда вы используете аннотацию @MockBean, ваш контекст будет загружаться для каждого теста.Пожалуйста, обратитесь к этой проблеме GitHub .Цитата из этой страницы:

Среда тестирования Spring будет кэшировать ApplicationContext, когда это возможно, между запусками теста.Для кэширования контекст должен иметь точно эквивалентную конфигурацию.Всякий раз, когда вы используете @MockBean, вы по определению изменяете конфигурацию контекста.

Поэтому, когда вы используете свой ложный бин в разных тестах - контекст будет воссоздан каждый раз для вашего тестового класса.Так, если у вас, например, есть некоторые bean-компоненты, которые загружают данные в БД при создании контекста - например, bean-компоненты для flyway - они будут создаваться при каждом воссоздании контекста.

1 голос
/ 29 мая 2019

Вот как я решил эту проблему (которую я считаю проблемой).

Решение 1: Я создал MockConfig класс с этим должен создать один mock для всего набора тестов:

@Configration
public class MockConfig {

  @Bean
  @Primary
  public ExternalService externalService() {
    return mock(ExternalService.class);
  }
}

А в тесте я просто автоматически подключаю внешний сервис:

@Autowire
private ExternalService externalService;

Но у этого решения есть проблема, оно создаст настоящий бин, а затем переопределит его фиктивным бином. Если ваша внешняя служба подключается к внешним ресурсам при создании, и вам это не нужно, вам понадобится другое решение.

Решение 2: Создайте базовый абстрактный класс с @MockBean в нем:

@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest
@Transactional
@Rollback
public abstract class BaseIntegrationTest {
  @MockBean
  ExternalService externalService;
}

И расширить интеграционный тест из этого базового класса:

class FooTest extends BaseIntegrationTest {

  @Autowired
  private MyService myService;
}

class BarTest extends BaseIntegrationTest {

  @Autowired
  private MyService myService;
}

Теперь контекст не будет обновляться, поскольку он всегда один и тот же, и настоящий бин не будет создан.

...