Переопределение beans в тестах интеграции
Для моего приложения Spring -Boot я предоставляю RestTemplate, хотя файл @Configuration, поэтому я могу добавить разумные значения по умолчанию (ex Timeouts). Для моих интеграционных тестов я хотел бы высмеять RestTemplate, поскольку я не хочу подключаться к внешним сервисам - я знаю, какие ответы ожидать. Я попытался предоставить другую реализацию в пакете интеграции-теста в надежде, что последний переопределит реальную реализацию, но проверка журналов выглядит наоборот: реальная реализация переопределяет тестовую.
Как я могу убедиться, что тот из TestConfig используется?
Это мой конфигурационный файл:
@Configuration
public class RestTemplateProvider {
private static final int DEFAULT_SERVICE_TIMEOUT = 5_000;
@Bean
public RestTemplate restTemplate(){
return new RestTemplate(buildClientConfigurationFactory());
}
private ClientHttpRequestFactory buildClientConfigurationFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(DEFAULT_SERVICE_TIMEOUT);
factory.setConnectTimeout(DEFAULT_SERVICE_TIMEOUT);
return factory;
}
}
Тест интеграции:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@WebAppConfiguration
@ActiveProfiles("it")
public abstract class IntegrationTest {}
Класс TestConfiguration:
@Configuration
@Import({Application.class, MockRestTemplateConfiguration.class})
public class TestConfiguration {}
И, наконец, MockRestTemplateConfiguration
@Configuration
public class MockRestTemplateConfiguration {
@Bean
public RestTemplate restTemplate() {
return Mockito.mock(RestTemplate.class)
}
}
Ответы
Ответ 1
Так как Spring Boot 1.4.x существует возможность использовать аннотацию @MockBean
для подделки Spring beans.
Реакция на комментарий:
Чтобы сохранить контекст в кеше, не используйте @DirtiesContext
, но используйте @ContextConfiguration(name = "contextWithFakeBean")
, и он создаст отдельный контекст, в то время как он будет поддерживать контекст по умолчанию в кеше. Spring сохранит оба (или сколько у вас контекстов) в кеше.
Наша сборка осуществляется таким образом, когда большинство тестов используют неподлинную конфигурацию по умолчанию, но у нас есть 4-5 тестов, которые притворяются beans. По умолчанию контекст красиво используется повторно
Ответ 2
1.
Вы можете использовать аннотацию @Primary
:
@Configuration
public class MockRestTemplateConfiguration {
@Bean
@Primary
public RestTemplate restTemplate() {
return Mockito.mock(RestTemplate.class)
}
}
Кстати, я написал сообщение в блоге о подделке Spring bean
2.
Но я бы предложил посмотреть Spring Поддержка тестирования RestTemplate. Это будет простой пример: частный MockRestServiceServer mockServer;
@Autowired
private RestTemplate restTemplate;
@Autowired
private UsersClient usersClient;
@BeforeMethod
public void init() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
@Test
public void testSingleGet() throws Exception {
// GIVEN
int testingIdentifier = 0;
mockServer.expect(requestTo(USERS_URL + "/" + testingIdentifier))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(TEST_RECORD0, MediaType.APPLICATION_JSON));
// WHEN
User user = usersClient.getUser(testingIdentifier);
// THEN
mockServer.verify();
assertEquals(user.getName(), USER0_NAME);
assertEquals(user.getEmail(), USER0_EMAIL);
}
Дополнительные примеры можно найти в моем репозитории Github здесь
Ответ 3
Пройдя немного глубже, посмотрите мой второй ответ.
Я решил проблему с помощью
@SpringBootTest(classes = {AppConfiguration.class, AppTestConfiguration.class})
вместо
@Import({ AppConfiguration.class, AppTestConfiguration.class });
В моем случае тест находится не в том же пакете, что и приложение. Поэтому мне нужно явно указать AppConfiguration.class (или App.class). Если вы используете один и тот же пакет в тесте, чем, я думаю, вы могли бы просто написать
@SpringBootTest(classes = AppTestConfiguration.class)
вместо (не работает)
@Import(AppTestConfiguration.class );
Это довольно сложно увидеть, что это так по-другому. Может быть, кто-то может объяснить это. Я не мог найти хорошие ответы до сих пор. Вы можете подумать, что @Import(...)
не @SpringBootTests
если присутствует @SpringBootTests
, но в журнале отображается переопределенный компонент. Но как раз наоборот.
Кстати, использование @TestConfiguration
вместо @Configuration
также не имеет значения.
Ответ 4
Проблема в вашей конфигурации заключается в том, что вы используете @Configuration
для своей тестовой конфигурации. Это заменит вашу основную конфигурацию. Вместо этого используйте @TestConfiguration
которая добавит (переопределит) вашу основную конфигурацию.
46.3.2 Определение конфигурации теста
Если вы хотите настроить первичную конфигурацию, вы можете использовать вложенный класс @TestConfiguration. В отличие от вложенного класса @Configuration, который будет использоваться вместо первичной конфигурации ваших приложений, в дополнение к первичной конфигурации ваших приложений используется вложенный класс @TestConfiguration.
Пример использования SpringBoot:
Основной класс
@SpringBootApplication() // Will scan for @Components and @Configs in package tree
public class Main{
}
Основной конфиг
@Configuration
public void AppConfig() {
// Define any beans
}
Тестовый конфиг
@TestConfiguration
public void AppTestConfig(){
// override beans for testing
}
Тестовый класс
@RunWith(SpringRunner.class)
@Import(AppTestConfig.class)
@SpringBootTest
public void AppTest() {
// use @MockBean if you like
}
Примечание: знайте, что будут созданы все Бины, даже те, которые вы переопределяете. Используйте @Profile
если вы не хотите создавать экземпляр @Configuration
.
Ответ 5
Проверьте этот ответ вместе с другими, предоставленными в этой теме. Это о переопределении bean-компонента в Spring Boot 2.X, где эта опция была отключена по умолчанию. В нем также есть некоторые идеи о том, как использовать Bean Definition DSL, если вы решили пойти по этому пути.