testcontainers / testcontainers-java

Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
https://testcontainers.org
MIT License
8.05k stars 1.66k forks source link

[Feature]: JUnit Jupiter extension for reuse of containers #6401

Open hendrikebbers opened 1 year ago

hendrikebbers commented 1 year ago

Module

Core

Problem

Assume you have several integration tests splitted in some classes. All this tests can reuse the same container instance. If the container needs some time to setup that would be a great benefit.

Solution

By using the JUnit extension API it would be easy to create a custom annotation like this:

@ContainerConfig(name="containerA", needNewInstance=false) public void testFoo() {}

A container that is needed could be simply defined by a provider:

@ContainerProvider(name="containerA") public GenericContainer<?> createContainerA() {}

By using test lifecycle callbacks (https://junit.org/junit5/docs/current/user-guide/#extensions-lifecycle-callbacks) this could be achieved as a JUnit extension.

I would be happy to help by creating the feature :)

Benefit

Containers that are needed for multiple tests in multiple classes only need to be defined once and the instances can be reused.

Alternatives

-

Would you like to help contributing this feature?

Yes

kiview commented 1 year ago

The use case makes a lot of sense, but I probably don't see this feature like this going into our JUnit Jupiter extension for now, because of a conceptual overlap with Reusable Containers.

It looks a little bit like dependency injections for containers to me. While I understand that it is a useful feature, I have a gut feeling that it might end up more complex as expected. I'd like to discuss if your use case can be implemented with our existing reuse capabilities and if not, and we think exploring this idea is worth a shot, doing it initially in a dedicated repo and as a dedicated extension might make more sense, to have more experimentation freedom to get it right.

lennart-bos commented 3 months ago

I have a similar request. I made it work by using JUnit suites. I want to reuse containers across all tests within a @Suite, without impacting single test runs, i.e. always stop all containers. I made this work, albeit ugly, by adding the following to the abstract superclass of each test:

  public static final SomeContainer CONTAINER_1 = new SomeContainer().withReuse(true);
  public static final SomeOtherContainer CONTAINER_2 = new SomeOtherContainer().withReuse(true);
  public static final YetOtherContainer CONTAINER_3 = new YetOtherContainer().withReuse(true);

  @BeforeAll
  static void startContainers() {
    CONTAINER_1.start();
    CONTAINER_2.start();
    CONTAINER_3.start();
  }

  @AfterAll
  static void stopContainers() {
    if (!TestcontainersConfiguration.getInstance().environmentSupportsReuse()) {
      CONTAINER_1.stop();
      CONTAINER_2.stop();
      CONTAINER_3.stop();
    }
  }

And the following to the suite:

  @BeforeSuite
  static void beforeSuite() {
    TestContainersUtil.setReuseEnabled(true);
  }

  @AfterSuite
  static void afterSuite() {
    CONTAINER_1.stop();
    CONTAINER_2.stop();
    CONTAINER_3.stop();

    TestContainersUtil.setReuseEnabled(false);
  }

Where the util is like this:

public class TestContainersUtil {

  private static final File PATH =
      new File(System.getProperty("user.home"), ".testcontainers.properties");

  public static void setReuseEnabled(boolean reuseEnabled) {
    try {
      Properties properties = new Properties();
      properties.load(new FileInputStream(PATH));
      properties.setProperty("testcontainers.reuse.enable", String.valueOf(reuseEnabled));
      properties.store(new FileOutputStream(PATH), null);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }
}

So, when running the suite, it starts the containers as soon as the first test starts and doesn't stop them after each test, only after the whole suite. When running a single test, it starts/stops the containers before/after that test.

I would prefer a more elegant way. Even just being able to simply control testcontainers.reuse.enable using System.setProperty would be nice.