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
The User
entity record/class is declared as
import org.springframework.data.annotation.Id;
public record User(
@Id
Long id,
String identifier,
String issuer
) {
}
And finally, the CRUD repository interface:
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Long> {
}
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();
}
}
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.
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 {
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
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");
Now the test passes!
Top comments (0)