С Переопределение bean-компонентов Spring Boot 2.1 отключено по умолчанию, что хорошо.
Однако у меня есть несколько тестов, в которых я заменяю beans на имитируемые экземпляры с помощью Mockito. При настройке по умолчанию тесты с такой конфигурацией завершатся ошибкой из-за переопределения bean-компонентов.
Единственный способ, который я обнаружил, сработал, - это включить переопределение bean-компонентов через свойства приложения:
spring.main.allow-bean-definition-overriding=true
Однако мне бы очень хотелось обеспечить минимальную настройку определения bean-компонентов для моей тестовой конфигурации, что будет указано весной с отключенным переопределением.
Бобы, которые я переопределяю, либо
То, что я думал, должно работать в тестовой конфигурации, переопределяя bean-компонент и вставляя на него @Primary, как мы привыкли для конфигураций источников данных. Однако это не имеет никакого эффекта и заставило меня задуматься: противоречат ли @Primary и отключенный bean-компонент?
Пример:
package com.stackoverflow.foo;
@Service
public class AService {
}
package com.stackoverflow.foo;
public class BService {
}
package com.stackoverflow.foo;
@Configuration
public BaseConfiguration {
@Bean
@Lazy
public BService bService() {
return new BService();
}
}
package com.stackoverflow.bar;
@Configuration
@Import({BaseConfiguration.class})
public class TestConfiguration {
@Bean
public BService bService() {
return Mockito.mock(BService.class);
}
}
Комментарий М. Дейнума, приведенный выше, действительно решает проблему, но только тогда, когда вы действительно используете моки. Когда вам нужно переопределить bean-компонент в тестах - это не помогает.
Замечу, что использование @MockBean в определенных тестах вызывает создание нового контекста (вместо повторного использования кешированного) и приводит к более медленному запуску тестов.




Переопределение bean-компонентов означает, что в контексте может быть только один bean-компонент с уникальным именем или идентификатором. Таким образом, вы можете предоставить два bean-компонента следующим образом:
package com.stackoverflow.foo;
@Configuration
public class BaseConfiguration {
@Bean
@Lazy
public BService bService1() {
return new BService();
}
}
package com.stackoverflow.bar;
@Configuration
@Import({BaseConfiguration.class})
public class TestConfiguration {
@Bean
public BService bService2() {
return Mockito.mock(BService.class);
}
}
Если вы добавите @Primary, то первичный компонент будет внедрен по умолчанию в:
@Autowired
BService bService;
Привет, у меня тоже не работает. А что делать, если BaseConfiguration не публичный, а частный пакет?
По умолчанию разрешено заменять @Component на @Bean. В твоем случае
@Service
public class AService {
}
@Component
public class BService {
@Autowired
public BService() { ... }
}
@Configuration
@ComponentScan
public BaseConfiguration {
}
@Configuration
// WARNING! Doesn't work with @SpringBootTest annotation
@Import({BaseConfiguration.class})
public class TestConfiguration {
@Bean // you allowed to override @Component with @Bean.
public BService bService() {
return Mockito.mock(BService.class);
}
}
Мне все еще нужно применить это свойство: spring.main.allow-bean-definition-overriding=true, а также @SpringBootTest(classes = {Application.class, TestConfiguration.class}), чтобы оно работало.
Определение класса TestConfiguration в @SpringBootTest наконец помогло мне. Спасибо Дык Тран
spring.main.allow-bean-definition-overriding=true можно разместить в тестовых конфигурациях. Если вам нужно обширное интеграционное тестирование, вам нужно будет в какой-то момент переопределить bean-компоненты. Это неизбежно.
Хотя правильный ответ уже был предоставлен, это означает, что у вашего bean-компонента будут разные имена. Так что технически это не переопределение.
Если вам нужно реальное переопределение (потому что вы используете @Qualifiers, @Resources или что-то подобное), поскольку Spring Boot 2.X возможен только с использованием свойства spring.main.allow-bean-definition-overriding=true.
Обновлять: Будьте осторожны с Kotlin Bean Definition DSL. В Spring Boot потребуется настраиваемый ApplicationContextInitializer, например:
class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {
override fun initialize(context: GenericApplicationContext) =
beans.initialize(context)
}
Теперь, если вы решите переопределить один из таких компонентов на основе DSL в своем тесте с помощью метода @Primary @Bean, этого не будет. Инициализатор сработает после методов @Bean, и вы все равно получите начальный компонент на основе DSL в своих тестах даже с @Primary на тестовом @Bean.
Еще один вариант - также создать инициализатор теста для ваших тестов и перечислить их все в ваших свойствах теста, например (порядок имеет значение):
context:
initializer:
classes: com.yuranos.BeansInitializer, com.yuranos.TestBeansInitializer
DSL определения компонента также поддерживает первичное свойство через:
bean(isPrimary=true) {...}
- что вам нужно для устранения двусмысленности при попытке внедрить bean-компонент, однако main:allow-bean-definition-overriding: true не нужен, если вы идете по пути чистого DSL.
(Весенняя загрузка 2.1.3)
Я делаю тестовые bean-компоненты доступными только в профиле test и разрешаю переопределение только во время тестирования, например:
@ActiveProfiles("test")
@SpringBootTest(properties = {"spring.main.allow-bean-definition-overriding=true"})
class FooBarApplicationTests {
@Test
void contextLoads() {}
}
Бин, над которым я издеваюсь в тестовой конфигурации:
@Profile("test")
@Configuration
public class FooBarApplicationTestConfiguration {
@Bean
@Primary
public SomeBean someBean() {
return Mockito.mock(SomeBean.class);
}
}
Вместо того, чтобы предоставлять свою собственную конфигурацию с макетом, почему бы не использовать
@MockBeanи позволить Spring Boot сделать замену. Поэтому вместо@Autowired BService bServiceсделайте@MockBean BService bServiceв своем коде. Сохраняет конфигурацию только для тестирования.