Spring Boot, Testcontainers, Oracle XE

In this blog post I will be showing the bare bones code you need to run an Integration test using Testcontainers in a Spring Boot application. I won't go into detail about how testing with the same database as you use in production is something you should do if you truly care about your software. I am using an Oracle XE database (not suprising I suppose) but you can subsitute that freely for any of Testcontainers supported databases.

Photo by Scott Graham / Unsplash

Let's start off with the pom.xml. Update your <properties>versions as you need. I am using an Oracle XE database for my tests so update that dependency for whichever database you're using.

	<properties>
        <junit.jupiter.version>5.4.2</junit.jupiter.version>
        <testcontainers.version>1.16.0</testcontainers.version>
    </properties>
    <dependencies>
				...
        <!--    Test    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>oracle-xe</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>testcontainers</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
				...
    </dependencies>

		<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>${junit.jupiter.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.testcontainers</groupId>
                <artifactId>testcontainers-bom</artifactId>
                <version>${testcontainers.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

Create a @TestConfiguration class to overwrite the DataSource bean. This needs to be manually imported because Spring Boot does not do component scanning on classes annotated with @TestConfiguration. Also notice we need to import the container (oracle) from its definition. This is the simplest override possible as Liquibase will also pickup on the DataSource and run your migrations with that connection information. While we could assume the username and password the port will always be random so it is best to just set your DataSource manually.

@TestConfiguration
public class OracleDbConfiguration {
    @Bean
    DataSource dataSource() {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl(oracle.getJdbcUrl());
        hikariConfig.setUsername(oracle.getUsername());
        hikariConfig.setPassword(oracle.getPassword());
        return new HikariDataSource(hikariConfig);
    }
}

Lets look at an example test file:

  • @Import(OracleDbConfiguration.class) is required to overwrite the datasource
  • @@ActiveProfiles("oracle") - is optional but a nice idea to do since container startup is not the quickest
@SpringBootTest
@Testcontainers
@Import(OracleDbConfiguration.class)
@ActiveProfiles("oracle")
public class OracleTestContainerTest {
    @Autowired
    MyService service;
    @Autowired
    MyRepository repository;

    @Container
    static final OracleContainer oracle = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim").withEnv("ORACLE_PASSWORD", "oracle");

    @Test
		public void myFancyTest(){
			assertThat(repository.findAll().size()).isEqualTo(0);
		}
}

And that's it! Now you can startup a full database container to run your integration tests with. If your database is specialized (ie requires users with certain permissions or objects installed organization wide) a great next step is to create your own docker image from the gvenzl/oracle-xe:18.4.0-slim with all your setup already done and the database created. This will speed up the start of your tests greatly.

References: