title: Using Testcontainers with JUnit5 extensions
body: <p>I using <code>Spring boot</code> and <code>JUnit5</code>. My app work with several databases: <code>MySQL</code>, <code>Clickhouse</code>, etc.</p>
  <p>For integration testing, I created JUnit5 Extensions:</p>
  <pre class="lang-java prettyprint-override"><code>public class ClickHouseTestContainersExtension implements BeforeAllCallback, BeforeTestExecutionCallback {
  
      public static final String IMAGE_NAME = &quot;registry.mycomp.com/db/clickhouse-server:20.5.3.27&quot;;
  
      @Container
      private static final FixedHostPortGenericContainer&lt;?&gt; clickHouse = new FixedHostPortGenericContainer&lt;&gt;(IMAGE_NAME)
              .withCopyFileToContainer(MountableFile.forClasspathResource(&quot;dbInitScripts/init_clickhouse.sql&quot;), &quot;/docker-entrypoint-initdb.d/init_clickhouse.sql&quot;)
              .withMinimumRunningDuration(Duration.ofSeconds(7))
              .withFixedExposedPort(8124, 8123);
  
      @Override
      public void beforeAll(ExtensionContext extensionContext) {
          startContainerIfNeed();
      }
  
      @Override
      public void beforeTestExecution(ExtensionContext extensionContext) {
          startContainerIfNeed();
      }
  
      public void startContainerIfNeed() {
          if (!clickHouse.isRunning()) {
              log.info(&quot;ClickHouse container is not running! Started.....&quot;);
              clickHouse.start();
          }
      }
  }
  </code></pre>
  <p>And MySQL:</p>
  <pre class="lang-java prettyprint-override"><code>public class MySQLTestContainersExtension implements BeforeAllCallback, BeforeTestExecutionCallback {
  
      private static final String IMAGE_NAME = &quot;registry.mycomp.com/db/mariadb:10.4.11&quot;;
  
      @Container
      private static final FixedHostPortGenericContainer&lt;?&gt; mariaDb = new FixedHostPortGenericContainer&lt;&gt;(IMAGE_NAME)
              .waitingFor(new LogMessageWaitStrategy()
                      .withRegEx(&quot;.*ready for connections.*&quot;)
                      .withTimes(2)
                      .withStartupTimeout(Duration.of(3, MINUTES)))
              .withEnv(&quot;MYSQL_DATABASE&quot;, &quot;db&quot;)
              .withEnv(&quot;MYSQL_USER&quot;, &quot;sys&quot;)
              .withEnv(&quot;MYSQL_PASSWORD&quot;, &quot;qwerty&quot;)
              .withEnv(&quot;MYSQL_ROOT_PASSWORD&quot;, &quot;toor&quot;)
              .withEnv(&quot;MYSQL_RANDOM_ROOT_PASSWORD&quot;, &quot;no&quot;)
              .withEnv(&quot;MYSQL_ALLOW_EMPTY_PASSWORD&quot;, &quot;no&quot;)
              .withFixedExposedPort(33060, 3306)
              .withCopyFileToContainer(MountableFile.forClasspathResource(&quot;dbInitScripts/init_mysql.sql&quot;),
                      &quot;/docker-entrypoint-initdb.d/init_mysql.sql&quot;);
  
      @Override
      public void beforeAll(ExtensionContext extensionContext) {
          startContainerIfNeed();
      }
  
      @Override
      public void beforeTestExecution(ExtensionContext extensionContext) {
          startContainerIfNeed();
      }
  
      public void startContainerIfNeed() {
          if (!mariaDb.isRunning()) {
              log.info(&quot;MariaDB container is not running! Started.....&quot;);
              mariaDb.start();
          }
      }
  }
  </code></pre>
  <p>And a number of other similar extensions.</p>
  <p>I also have annotations:</p>
  <pre class="lang-java prettyprint-override"><code>@Target(value = {ElementType.TYPE, ElementType.METHOD})
  @Retention(RUNTIME)
  @ExtendWith({MySQLTestContainersExtension.class})
  public @interface MySQL {
  }
  
  @Target(value = {ElementType.TYPE, ElementType.METHOD})
  @Retention(RUNTIME)
  @ExtendWith({ClickHouseTestContainersExtension.class})
  public @interface ClickHouse {
  }
  
  @Target(value = {ElementType.TYPE, ElementType.METHOD})
  @Retention(RUNTIME)
  @ExtendWith({ClickHouseTestContainersExtension.class, MySQLTestContainersExtension.class,
          PostgreSQLTestContainersExtension.class, IgniteTestContainersExtension.class})
  public @interface WithAllDatabases {
  }
  </code></pre>
  <p>And, When I write some test, I use this extension:</p>
  <pre class="lang-java prettyprint-override"><code>
      @Test
      @ClickHouse
      @DisplayName(&quot;Test connection ClickHouse with alive status&quot;)
      void testConnectionClickHouseWithAliveStatus() throws IOException {
          //smth
      }
  
      @Test
      @MySQL
      @DisplayName(&quot;Test connection MySQL with alive status&quot;)
      void testConnectionMariaDBWithAliveStatus() throws IOException {
          //smth
      }
  
    //etc
  </code></pre>
  <p>My connections properties, like username, password, url, store in database, not in apllication.yml. Any user of my system, can mannualy add new connection.</p>
  <p>So I can't just use a spring mechanism like a <code>@DynamicPropertySource</code>.</p>
  <p>Moreover, I can't get the generated random port from the extension in any way!</p>
  <p>Therefore, I use a fixed port to create a set of test connections to the database raised in the content in tests!</p>
  <p>Tell me if there is some approach that will allow you to use the <code>JUnit 5</code> extension and use a random port. My task was to raise any database for a specific test by simply putting an annotation above this test.</p>