Where to start an integration test database with Spring Boot












0















Instead of using JUnit @ClassRules to start docker TestContainers for our integration test database, we let Spring do it with ApplicationContextInitializers.

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 a TestSuite)

  • 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.










share|improve this question

























  • 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
















0















Instead of using JUnit @ClassRules to start docker TestContainers for our integration test database, we let Spring do it with ApplicationContextInitializers.

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 a TestSuite)

  • 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.










share|improve this question

























  • 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














0












0








0








Instead of using JUnit @ClassRules to start docker TestContainers for our integration test database, we let Spring do it with ApplicationContextInitializers.

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 a TestSuite)

  • 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.










share|improve this question
















Instead of using JUnit @ClassRules to start docker TestContainers for our integration test database, we let Spring do it with ApplicationContextInitializers.

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 a TestSuite)

  • 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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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 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



















  • 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

















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












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
});


}
});














draft saved

draft discarded


















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
















draft saved

draft discarded




















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

Monofisismo

Angular Downloading a file using contenturl with Basic Authentication

Olmecas