DEV Community

Geoff Bourne
Geoff Bourne

Posted on

Spring Boot DataJdbcTest with PostgreSQL and Liquibase

In this article I wanted to share a little journey I took creating a Spring Boot unit test that exercises a Spring Data JDBC repository running against a Postgresql database initialized with Liquibase. I made a couple of mistakes along the way that I wanted to document here and provide the solutions for those.

In the Spring Initializr I chose the dependencies:

  • Spring Data JDBC
  • Liquibase Migration
  • PostgreSQL Driver

To initialize the schema, I created the Liquibase changelog file at src/main/resources/db/changelog/db.changelog-master.yaml where I create a user table:

databaseChangeLog:
  - changeSet:
      id: 1
      author: gdb
      changes:
        - createTable:
            tableName: user
            columns:
              - column:
                  name: id
                  autoIncrement: true
                  type: BIGINT
                  constraints:
                    primaryKey: true
                    nullable: false
              - column:
                  name: identifier
                  type: VARCHAR(50)
                  constraints:
                    nullable: false
              - column:
                  name: issuer
                  type: VARCHAR(100)
                  constraints:
                    nullable: true
Enter fullscreen mode Exit fullscreen mode

The User entity record/class is declared as

import org.springframework.data.annotation.Id;

public record User(
    @Id
    Long id,
    String identifier,
    String issuer
) {
}
Enter fullscreen mode Exit fullscreen mode

And finally, the CRUD repository interface:

import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Long> {
}
Enter fullscreen mode Exit fullscreen mode

At first I started with the test class written as

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import static org.assertj.core.api.Assertions.assertThat;

@Testcontainers
@DataJdbcTest
class UserRepositoryTest {
    @Container
    static PostgreSQLContainer<?> pg = 
        new PostgreSQLContainer<>("postgres:16");

    @Autowired
    UserRepository userRepository;

    @Test
    void saveUser() {
        final User result = userRepository.save(
            new User(null, "name1", "issuer1")
        );

        assertThat(result.id()).isNotNull();
    }
}
Enter fullscreen mode Exit fullscreen mode

but that resulted in the error

Error creating bean with name 'liquibase' defined in class path resource
[org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]: 
Failed to instantiate [liquibase.integration.spring.SpringLiquibase]: 
Factory method 'liquibase' threw exception with message: 
Error creating bean with name 'dataSource': 
Failed to replace DataSource with an embedded database for tests. 
If you want an embedded database please put a supported one on the classpath 
or tune the replace attribute of @AutoConfigureTestDatabase.
Enter fullscreen mode Exit fullscreen mode

It was one of those errors that actually tells you the solution. Nice! So, I added:

@Testcontainers
@AutoConfigureTestDatabase(
    replace = AutoConfigureTestDatabase.Replace.NONE
)
@DataJdbcTest
class UserRepositoryTest {
Enter fullscreen mode Exit fullscreen mode

The next error was

Error creating bean with name 'dataSource' defined in class path resource 
[org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: 
Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: 
Factory method 'dataSource' threw exception with message: 
Failed to determine a suitable driver class
Enter fullscreen mode Exit fullscreen mode

I remembered that Spring Boot added the service connections feature, so added that to the container field's declaration:

    @Container
    @ServiceConnection
    static PostgreSQLContainer<?> pg = 
        new PostgreSQLContainer<>("postgres:16");
Enter fullscreen mode Exit fullscreen mode

Now the test passes!

Top comments (0)