I use the spring-boot-starter-web and spring-boot-starter-test.
Let\'s say I have a class for binding configuration properties:
@ConfigurationPropert
Why is that an integration test to begin with? Why are you starting a full blown Spring Boot app for that?
This looks like unit testing to me. That being said, you have several options:
@IntegrationTest
and Spring Boot will not start a web server to begin with (use @PropertySource
to pass value to your test but it feels wrong to pass an invalid value to your whole test class)spring.main.web-environment=false
to disable the web server (but that's silly given the point above)DummyProperties
of yours. You don't even need to start a Spring Boot application for that. Look at our own test suiteI'd definitely go with the last one. Maybe you have a good reason to have an integration test for that?
ApplicationContextRunner
It is described in Spring Boot Reference Documentation:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html#boot-features-test-autoconfig
And there is a quick guide about it:
https://www.baeldung.com/spring-boot-context-runner
private static final String POSITIVE_CASE_CONFIG_FILE =
"classpath:some/path/positive-case-config.yml";
private static final String NEGATIVE_CASE_CONFIG_FILE =
"classpath:some/path/negative-case-config.yml";
@Test
void positiveTest() {
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withInitializer(new ConfigDataApplicationContextInitializer())//1
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.DEBUG))//2
.withUserConfiguration(MockBeansTestConfiguration.class)//3
.withPropertyValues("spring.config.location=" + POSITIVE_CASE_CONFIG_FILE)//4
.withConfiguration(AutoConfigurations.of(BookService.class));//5
contextRunner
.run((context) -> {
Assertions.assertThat(context).hasNotFailed();//6
});
}
@Test
void negativeTest() {
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withInitializer(new ConfigDataApplicationContextInitializer())//1
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.DEBUG))//2
.withUserConfiguration(MockBeansTestConfiguration.class)//3
.withPropertyValues("spring.config.location=" + NEGATIVE_CASE_CONFIG_FILE)//4
.withConfiguration(AutoConfigurations.of(BookService.class));//5
contextRunner
.run((context) -> {
assertThat(context)
.hasFailed();
assertThat(context.getStartupFailure())
.isNotNull();
assertThat(context.getStartupFailure().getMessage())
.contains("Some exception message");
assertThat(extractFailureCauseMessages(context))
.contains("Cause exception message");
});
}
private List<String> extractFailureCauseMessages(AssertableApplicationContext context) {
var failureCauseMessages = new ArrayList<String>();
var currentCause = context.getStartupFailure().getCause();
while (!Objects.isNull(currentCause)) {//7
failureCauseMessages.add(currentCause.getMessage());
currentCause = currentCause.getCause();
}
return failureCauseMessages;
}
Explanation with examples of similar definitions from Junit5 Spring Boot Test Annotations:
application.properties
or application.yml
ConditionEvaluationReport
using given log level when application context fails@Autowired BookRepository
in our BookService
and we provide mock BookRepository
in MockBeansTestConfiguration
. Similar to @Import({MockBeansTestConfiguration.class})
in test class and @TestConfiguration
in class with mock beans in normal Junit5 Spring Boot Test@TestPropertySource(properties = { "spring.config.location=" + POSITIVE_CASE_CONFIG_FILE})
@ContextConfiguration(classes = {BookService.class})
or @SpringBootTest(classes = {BookService.class})
together with @Import({BookService.class})
in normal testAssertions.assertThat
, but I wanted to show where this method is fromObjects.isNull
, but I wanted to show where this method is fromMockBeansTestConfiguration class:
@TestConfiguration
public class MockBeansTestConfiguration {
private static final Book SAMPLE_BOOK = Book.of(1L, "Stanisław Lem", "Solaris", "978-3-16-148410-0");
@Bean
public BookRepository mockBookRepository() {
var bookRepository = Mockito.mock(BookRepository.class);//1
Mockito.when(bookRepository.findByIsbn(SAMPLE_BOOK.getIsbn()))//2
.thenReturn(SAMPLE_BOOK);
return bookRepository;
}
}
Remarks:
1,2. There should be static import, but I wanted to show where this method is from
I think the easiest way is:
public class InvalidUrlTest {
@Rule
public DisableOnDebug testTimeout = new DisableOnDebug(new Timeout(5, TimeUnit.SECONDS));
@Rule
public ExpectedException expected = ExpectedException.none();
@Test
public void shouldFailOnStartIfUrlInvalid() {
// configure ExpectedException
expected.expect(...
MyApplication.main("--dummy.url=123:456");
}
// other cases
}