Running SpringBootTest Context Without Testcontainers Launch

1k Views Asked by At

I have 2 parent test classes:

@SpringBootTest(properties = {
        "spring.datasource.url=jdbc:tc:mysql:8.0.25:///my_test_db?TC_INITSCRIPT=db/init_mysql.sql",
        "spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver"
})
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
public abstract class UserApplicationIntegrationTest {
}

and

@SpringBootTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
public abstract class UserApplicationTest {
}

The idea is for various test classes to extend these classes. The ones which require a mocked MySQL DB will extend UserApplicationIntegrationTest. Ones which don't need a DB connection but that do require a Spring context will extend UserApplicationTest.

In the absence of UserApplicationIntegrationTest, all the test classes extending UserApplicationTest work well, including using the Mockito framework. Unfortunately, when I introduce UserApplicationIntegrationTest and its sub-tests (which work perfectly with the dockerised db instance), these tests begin to fail as they suddenly demand a datasource.

Caused by: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class

If I try excluding datasource auto-configuration either in app properties or in annotations of the parent class, the testcontainers tests (those extending UserApplicationIntegrationTest) start failing because of a problem with the Spring context and not being able to autowire beans any longer in those tests.

Before I know it, I'm down a rabbit hole of attempting messy exclusions/additions that I've been down before in previous projects and it only leads to problems further down the line.

Essentially I want 3 types of tests coexisting in my project:

  1. Unit tests with no Spring context
  2. Unit tests with a Spring context (including lots of mocking but still autowiring/constructor injection support)
  3. Integration tests with a Spring context that spin up testcontainers and allow me to test DB interactions (and potentially end to end tests to come)

The original reason that I wanted to avoid launching testcontainers for all Spring context tests (which would 'work' perfectly well and only include 1 docker delay in the build process) was because it was irritating me to have to wait for the mysql connection to the dockerised instance every time I ran individual Spring context tests locally during development.

Is there a tidy way to achieve this or an altogether better way of navigating the requirement?

Thanks in advance.

1

There are 1 best solutions below

2
On

Hopefully I understand you right, what I did was implementing an abstract TestContainer test class:

package de.dwosch.it;

@ActiveProfiles("test")
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
    @ContextConfiguration(initializers = AbstractPostgreSQLTestContainerIT.Initializer.class)
    @Testcontainers
    public abstract class AbstractPostgreSQLTestContainerIT {
        private static final String POSTGRES_VERSION = "postgres:11.1";
        public static PostgreSQLContainer database;
    
        static {
            database = new PostgreSQLContainer(POSTGRES_VERSION);
            database.start();
        }
    
        static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
            @Override
            public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
                TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
                        configurableApplicationContext,
                        "spring.datasource.url=" + database.getJdbcUrl(),
                        "spring.datasource.username=" + database.getUsername(),
                        "spring.datasource.password=" + database.getPassword()
                );
            }
        }
    }

Then I just extend my test classes by this abstract class which will fire up a test container and the whole spring context for better separation

class MyAwesomeControllerIT extends AbstractPostgreSQLTestContainerIT { }