4

I have a docker DB setup method, which currently located in @BeforeAll. Currently, Construct as below

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public DockerConstructorTest{
  @BeforeAll
  public static void setup(){
    ...
    CreateContainer
    ...
  }

  @AfterAll
  public static void done(){
    ...
    Stop & Remove Container
    ...
  }
}

There're multiple test classes which all extends this Test super class, each test class will construct one container and remove it after it is done. Therefore, maven consumes a lot time to manage docker. (Creation and Removal)

My question is that whether there's better way to deal with it

The ideal case what I may want to achieve is that this container create & deletion only run once as before @SpringBootTest starts, it will be shared with all test classes. In the meanwhile, it would not stop other developer creating new container for some corner scenarios as well.

I have some incomplete idea:

  1. Add Constructor trigger within SpringBoot main class, if it is started by Test, run Docker container constructor. But it also means that I add some test related code in Main Class, making them coupled. Personally hate this happens
  2. Override SpringBootTest. Overriding bugs me that whether I should do.

Please share your brilliant ideas, I will be appreciated if it would solve this issue, or partial of this issue.

Neil
  • 2,714
  • 11
  • 29
  • 45

3 Answers3

1

If JUnit 5 is used, see the following answers (possible using custom extensions, first-class support will come later from the JUnit team)

Soufiane Sakhi
  • 809
  • 10
  • 13
1

Not sure what is your case exactly, however, I would suggest you using TestContainers with spring boot, there are many configurable containers that you can use out of the box. Moreover, they solve your problem, you can have Singleton containers. https://github.com/testcontainers/testcontainers-spring-boot

Here some reference on how to use it for several classes (Singleton containers), https://www.testcontainers.org/test_framework_integration/manual_lifecycle_control/

In our use case, we are using Spock/Groovy with TestContainers approximately our base class looks like this.

@SpringBootTest(classes = DonkeyApplication.class)
class AbstractSpringSpec extends Specification {
    @Shared
    Closure cleanupClosure
    @Shared
    boolean setupSpecDone = false

    def setup() {
        if (!setupSpecDone) {
            setupSpecWithSpring()
            setupSpecDone = true
        }
        cleanupClosure = this.&cleanupSpecWithSpring
    }

    def cleanupSpec() {
        cleanupClosure?.run()
    }

    def setupSpecWithSpring() {
        // override this if Spring Beans are needed in setupSpec
    }

    def cleanupSpecWithSpring() {
        // override this if Spring Beans are needed in cleanupSpec
    }
}

referance

Small reference what spock does on cleanup or setup: enter image description here

Hope this messy answer gives some answers to your questions!

VostanAzatyan
  • 618
  • 1
  • 9
  • 22
1

You should instantiate the containers in a static context. Something like:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public DockerConstructorTest{
    static PostgreSQLContainer<?> postgreSQLContainer =
      new PostgreSQLContainer<>(DockerImageName.parse("postgres:latest"))
          .withDatabaseName("db")
          .withUsername("postgres")
          .withPassword("postgres");

    static {
      postgreSQLContainer.start();
    }
}

That way, all of the classes that extend this class won't have to recreate the containers.

Mike B
  • 125
  • 1
  • 1
  • 10