Reputation: 6531
I am using Spring Boot and running tests in Testcontainers.
Sometimes (when developing) I would like to run tests not against Testcontainers, but against already running containers.
Is there a way to disable Testcontainers depending on Spring profiles, environment variables, etc.?
Right now I am commenting the container injection code and regularly check them in like that.
Upvotes: 5
Views: 7088
Reputation: 1
To test against test containers dependig on a spring active profile, I personally used the Spring ApplicationContextInitializer
interface to :
I created a class implementing ApplicationContextInitializer<ConfigurableApplicationContext>
interface and then referenced it in a@ContextConfiguration
annotation on the test class.
Here is an example class implenting ApplicationContextInitializer<ConfigurableApplicationContext>
to launch an ollama ai embedding container (here with a docker compose file) when the active profiles include "ollama-embedding":
package com.example.service;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.support.TestPropertySourceUtils;
import org.testcontainers.containers.DockerComposeContainer;
import java.io.File;
import java.util.Arrays;
import java.util.Objects;
public class TestContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final int OLLAMA_PORT = 11434;
private static final String OLLAMA_PROFILE = "ollama-embedding";
@Override
@SuppressWarnings({"resource", "HttpUrlsUsage"})
public void initialize(@NotNull ConfigurableApplicationContext applicationContext) {
if (Arrays.asList(applicationContext.getEnvironment().getActiveProfiles()).contains(OLLAMA_PROFILE)) {
final DockerComposeContainer<?> container = new DockerComposeContainer<>(
new File(Objects.requireNonNull(ProductServiceTests.class.getClassLoader()
.getResource("compose-test.yml")).getFile()))
.withExposedService("ollama_1", OLLAMA_PORT);
container.start();
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
applicationContext,
"spring.ai.ollama.base-url=http://" +
container.getServiceHost("ollama_1", OLLAMA_PORT) + ":" +
container.getServicePort("ollama_1", OLLAMA_PORT)
);
}
}
}
Here is the example test class referencing that class in a @ContextConfiguration
annotation:
package com.example.service;
import static org.assertj.core.api.Assertions.assertThat;
import com.example.Application;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
@SpringBootTest
@ContextConfiguration(initializers = TestContextInitializer.class, classes = Application.class)
@DirtiesContext
@ActiveProfiles({"another-profile", "ollama-embedding"})
public class IntegrationTests {
@Test
void test() {
// Your tests here
}
}
This way, the container(s) will start if and only if the target profile (here "ollama-embedding") is present in the active profiles list.
It is important that you replace the application properties dynamically, as shown in the TestContextInitializer class, so that the containers is (are) properly reached by the test code through Spring application context.
Upvotes: 0
Reputation: 6531
As recommended by Sergei here https://github.com/testcontainers/testcontainers-java/issues/2833#event-3405411419
this is the solution:
public class FixedHostPortGenericDisableableContainer<T extends FixedHostPortGenericDisableableContainer<T>> extends FixedHostPortGenericContainer<T> {
private boolean isActive;
public FixedHostPortGenericDisableableContainer(@NotNull String dockerImageName) {
super(dockerImageName);
}
@Override
public void start() {
if (isActive) {
super.start();
}
}
public FixedHostPortGenericDisableableContainer isActive(boolean isActive) {
this.isActive = isActive;
return this;
}
}
Usage
// set this environment variable to true to disable test containers
public static final String ENV_DISABLE_TEST_CONTAIENRS = "DISABLE_TEST_CONTAIENRS";
@Container
private static GenericContainer dynamoDb =
new FixedHostPortGenericDisableableContainer("amazon/dynamodb-local:1.11.477")
.isActive(StringUtils.isBlank(System.getenv(ENV_DISABLE_TEST_CONTAIENRS)))
.withFixedExposedPort(8001, 8000)
.withStartupAttempts(100);
Upvotes: 2
Reputation: 860
Yes, it can be done using profiles.
One possible solution would be (the idea is to play around with static
keyword and it assumes using .withLocalCompose(true)
):
@Configuration
@Profile("test")
public class TestDockerConfig {
// initialize your containers in static fields/static block
}
And use the test profile when you need it. Even if you import that configuration in all tests, it should only be loaded for "test" ones.
The idea is to have docker environment provided to test suite and use property profiles. It can either:
Since starting up containers takes time you want to do that only once in a static way and it will be loaded before all of your classes.
Hope that helps.
Upvotes: 2
Reputation: 122061
One way to get containers in your test is just to use the JDBC URL, per the docs. This allows you to easily switch between e.g. Testcontainers and localhost based on a profile:
application-integration.yml
spring.datasource.url: jdbc:tc:postgresql:12-alpine:///mydatabase
application-dev.yml
spring.datasource.url: jdbc:postgresql://localhost:5432/mydatabase
As the documentation notes:
- TC needs to be on your application's classpath at runtime for this to work
- For Spring Boot (Before version 2.3.0) you need to specify the driver manually
spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver
Upvotes: 1