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)