DEV Community

Geoff Bourne
Geoff Bourne

Posted on

9

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!

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay