Where to start an integration test database with Spring Boot
Instead of using JUnit @ClassRule
s to start docker TestContainers for our integration test database, we let Spring do it with ApplicationContextInitializer
s.
Benefits :
- start only 1 container for all "integration test" classes in a maven module thanks to TestContext caching (JUnit
ClassRule
and have each test class start a new container) - also works when launching a single test class from the IDE (unlike a JUnit
ClassRule
in aTestSuite
) - Resulting properties are injected early into the context : database IP, port, credentials... this way we don't override beans (
DataSource
,Session
, etc), we use the same configuration classes as the production application - the container object created is added to the context as a bean, so testing code can access it
Simplified example :
public class RedisContainerInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<ContextClosedEvent> {
private GenericContainer<?> container;
@Override
public void initialize(ConfigurableApplicationContext ctx) {
container = new GenericContainer<>("redis:3.2.12-alpine").withExposedPorts(6379);
container.start();
// Inject properties
TestPropertyValues props = TestPropertyValues.of(
"spring.redis.host=" + container.getContainerIpAddress(),
"spring.redis.port=" + container.getMappedPort(6379));
props.applyTo(ctx);
// Register this object as a bean,
// so testing code can access the container
ctx.getBeanFactory().registerSingleton("redisContainer", this);
// Make this instance receive Spring context "Close" events
ctx.addApplicationListener(this);
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
container.close();
}
// getter, other irrelevant methods
}
Question : Using an ApplicationContextInitializer
works but is it the "Spring way" to do so ?
If not, what kind of Spring Bean could be loaded early enough to inject properties that will be used by Spring Boot starters ?
At some point we used the Spring Cloud Bootstrap context, but users shouldn't need Spring Cloud to run integration tests.
spring spring-boot
add a comment |
Instead of using JUnit @ClassRule
s to start docker TestContainers for our integration test database, we let Spring do it with ApplicationContextInitializer
s.
Benefits :
- start only 1 container for all "integration test" classes in a maven module thanks to TestContext caching (JUnit
ClassRule
and have each test class start a new container) - also works when launching a single test class from the IDE (unlike a JUnit
ClassRule
in aTestSuite
) - Resulting properties are injected early into the context : database IP, port, credentials... this way we don't override beans (
DataSource
,Session
, etc), we use the same configuration classes as the production application - the container object created is added to the context as a bean, so testing code can access it
Simplified example :
public class RedisContainerInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<ContextClosedEvent> {
private GenericContainer<?> container;
@Override
public void initialize(ConfigurableApplicationContext ctx) {
container = new GenericContainer<>("redis:3.2.12-alpine").withExposedPorts(6379);
container.start();
// Inject properties
TestPropertyValues props = TestPropertyValues.of(
"spring.redis.host=" + container.getContainerIpAddress(),
"spring.redis.port=" + container.getMappedPort(6379));
props.applyTo(ctx);
// Register this object as a bean,
// so testing code can access the container
ctx.getBeanFactory().registerSingleton("redisContainer", this);
// Make this instance receive Spring context "Close" events
ctx.addApplicationListener(this);
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
container.close();
}
// getter, other irrelevant methods
}
Question : Using an ApplicationContextInitializer
works but is it the "Spring way" to do so ?
If not, what kind of Spring Bean could be loaded early enough to inject properties that will be used by Spring Boot starters ?
At some point we used the Spring Cloud Bootstrap context, but users shouldn't need Spring Cloud to run integration tests.
spring spring-boot
Why not useapplication-test.properties
to inject the test database details? If you need to launch the container as part of the integration test, you can create a script to launch the container and then run the Gradle task, right?
– Smajl
Jan 2 at 14:09
We have many integration test classes per application, and we want to be able to launch a single test from the IDE, multiple tests from the IDE, or multiple tests from maven/gradle. Using maven/gradle plugins, you can only launch tests using maven/gradle.
– Michael Técourt
Jan 2 at 14:35
add a comment |
Instead of using JUnit @ClassRule
s to start docker TestContainers for our integration test database, we let Spring do it with ApplicationContextInitializer
s.
Benefits :
- start only 1 container for all "integration test" classes in a maven module thanks to TestContext caching (JUnit
ClassRule
and have each test class start a new container) - also works when launching a single test class from the IDE (unlike a JUnit
ClassRule
in aTestSuite
) - Resulting properties are injected early into the context : database IP, port, credentials... this way we don't override beans (
DataSource
,Session
, etc), we use the same configuration classes as the production application - the container object created is added to the context as a bean, so testing code can access it
Simplified example :
public class RedisContainerInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<ContextClosedEvent> {
private GenericContainer<?> container;
@Override
public void initialize(ConfigurableApplicationContext ctx) {
container = new GenericContainer<>("redis:3.2.12-alpine").withExposedPorts(6379);
container.start();
// Inject properties
TestPropertyValues props = TestPropertyValues.of(
"spring.redis.host=" + container.getContainerIpAddress(),
"spring.redis.port=" + container.getMappedPort(6379));
props.applyTo(ctx);
// Register this object as a bean,
// so testing code can access the container
ctx.getBeanFactory().registerSingleton("redisContainer", this);
// Make this instance receive Spring context "Close" events
ctx.addApplicationListener(this);
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
container.close();
}
// getter, other irrelevant methods
}
Question : Using an ApplicationContextInitializer
works but is it the "Spring way" to do so ?
If not, what kind of Spring Bean could be loaded early enough to inject properties that will be used by Spring Boot starters ?
At some point we used the Spring Cloud Bootstrap context, but users shouldn't need Spring Cloud to run integration tests.
spring spring-boot
Instead of using JUnit @ClassRule
s to start docker TestContainers for our integration test database, we let Spring do it with ApplicationContextInitializer
s.
Benefits :
- start only 1 container for all "integration test" classes in a maven module thanks to TestContext caching (JUnit
ClassRule
and have each test class start a new container) - also works when launching a single test class from the IDE (unlike a JUnit
ClassRule
in aTestSuite
) - Resulting properties are injected early into the context : database IP, port, credentials... this way we don't override beans (
DataSource
,Session
, etc), we use the same configuration classes as the production application - the container object created is added to the context as a bean, so testing code can access it
Simplified example :
public class RedisContainerInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<ContextClosedEvent> {
private GenericContainer<?> container;
@Override
public void initialize(ConfigurableApplicationContext ctx) {
container = new GenericContainer<>("redis:3.2.12-alpine").withExposedPorts(6379);
container.start();
// Inject properties
TestPropertyValues props = TestPropertyValues.of(
"spring.redis.host=" + container.getContainerIpAddress(),
"spring.redis.port=" + container.getMappedPort(6379));
props.applyTo(ctx);
// Register this object as a bean,
// so testing code can access the container
ctx.getBeanFactory().registerSingleton("redisContainer", this);
// Make this instance receive Spring context "Close" events
ctx.addApplicationListener(this);
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
container.close();
}
// getter, other irrelevant methods
}
Question : Using an ApplicationContextInitializer
works but is it the "Spring way" to do so ?
If not, what kind of Spring Bean could be loaded early enough to inject properties that will be used by Spring Boot starters ?
At some point we used the Spring Cloud Bootstrap context, but users shouldn't need Spring Cloud to run integration tests.
spring spring-boot
spring spring-boot
edited Jan 2 at 14:36
Michael Técourt
asked Jan 2 at 13:32
Michael TécourtMichael Técourt
2,2732035
2,2732035
Why not useapplication-test.properties
to inject the test database details? If you need to launch the container as part of the integration test, you can create a script to launch the container and then run the Gradle task, right?
– Smajl
Jan 2 at 14:09
We have many integration test classes per application, and we want to be able to launch a single test from the IDE, multiple tests from the IDE, or multiple tests from maven/gradle. Using maven/gradle plugins, you can only launch tests using maven/gradle.
– Michael Técourt
Jan 2 at 14:35
add a comment |
Why not useapplication-test.properties
to inject the test database details? If you need to launch the container as part of the integration test, you can create a script to launch the container and then run the Gradle task, right?
– Smajl
Jan 2 at 14:09
We have many integration test classes per application, and we want to be able to launch a single test from the IDE, multiple tests from the IDE, or multiple tests from maven/gradle. Using maven/gradle plugins, you can only launch tests using maven/gradle.
– Michael Técourt
Jan 2 at 14:35
Why not use
application-test.properties
to inject the test database details? If you need to launch the container as part of the integration test, you can create a script to launch the container and then run the Gradle task, right?– Smajl
Jan 2 at 14:09
Why not use
application-test.properties
to inject the test database details? If you need to launch the container as part of the integration test, you can create a script to launch the container and then run the Gradle task, right?– Smajl
Jan 2 at 14:09
We have many integration test classes per application, and we want to be able to launch a single test from the IDE, multiple tests from the IDE, or multiple tests from maven/gradle. Using maven/gradle plugins, you can only launch tests using maven/gradle.
– Michael Técourt
Jan 2 at 14:35
We have many integration test classes per application, and we want to be able to launch a single test from the IDE, multiple tests from the IDE, or multiple tests from maven/gradle. Using maven/gradle plugins, you can only launch tests using maven/gradle.
– Michael Técourt
Jan 2 at 14:35
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54007303%2fwhere-to-start-an-integration-test-database-with-spring-boot%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54007303%2fwhere-to-start-an-integration-test-database-with-spring-boot%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Why not use
application-test.properties
to inject the test database details? If you need to launch the container as part of the integration test, you can create a script to launch the container and then run the Gradle task, right?– Smajl
Jan 2 at 14:09
We have many integration test classes per application, and we want to be able to launch a single test from the IDE, multiple tests from the IDE, or multiple tests from maven/gradle. Using maven/gradle plugins, you can only launch tests using maven/gradle.
– Michael Técourt
Jan 2 at 14:35