Skip
Skip

Reputation: 6531

Is there a way to disable Testcontainers depending on Spring profile?

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

Answers (4)

To test against test containers dependig on a spring active profile, I personally used the Spring ApplicationContextInitializer interface to :

  1. Get the application context active profiles,
  2. Depending on the target profile being present or not in the active profiles, list, start the container(s) and inject the replacement application properties based on the test container(s) dynamic properties (e.g. host, port, ...).

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

Skip
Skip

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

Vitaly Chura
Vitaly Chura

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:

  • be provided by local docker engine ("dev") where you start containers yourself with proper dev URLs specified in application-dev.properties
  • or provided via TestContainers, with test URLs in application-test.properties

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

jonrsharpe
jonrsharpe

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

Related Questions