Let’s get started with a simple Spring Boot application using Spring Data JPA and H2 dependencies.
The goal of the application is to keep track of music albums.
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Album {
@Id
@GeneratedValue
private long id;
private String name;
private String artist;
public Album() {
}
public Album(String name, String artist) {
this.name = name;
this.artist = artist;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public String getArtist() {
return artist;
}
}
The corresponding repository:
import org.springframework.data.repository.CrudRepository;
public interface AlbumRepository extends CrudRepository<Album, Long> {
}
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
class AlbumRepositoryTest {
private final AlbumRepository repository;
@Autowired
public AlbumRepositoryTest(AlbumRepository repository) {
this.repository = repository;
}
@Test
void testSaveAlbum() {
Album album = repository.save(new Album("Master of Puppets", "Metallica"));
assertThat(album).isNotNull()
.extracting(Album::getId)
.isInstanceOfSatisfying(Long.class,
id -> assertThat(id).isPositive());
}
}
Note the @DataJpaTest
annotation.
When the test executes, Spring will create an application context and will instantiate all @Repository
classes in our application and all supporting infrastructure (Hibernate, H2, …).
Now, to make things a bit more realistic, we will use PostgreSQL instead of H2 as database and
use Flyway for database migrations. To make it easy to test this setup, we will use Testcontainers.
If we do that, our test suddenly becomes something like this:
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.ActiveProfiles;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest (1)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) (2)
@Tag("db-test") (3)
@ActiveProfiles("data-jpa-test") (4)
class AlbumRepositoryTest {
private final AlbumRepository repository;
@Autowired
public AlbumRepositoryTest(AlbumRepository repository) {
this.repository = repository;
}
@Test
void testSaveAlbum() {
Album album = repository.save(new Album("Master of Puppets", "Metallica"));
assertThat(album).isNotNull()
.extracting(Album::getId)
.isInstanceOfSatisfying(Long.class,
id -> assertThat(id).isPositive());
}
@TestConfiguration (5)
static class TestConfig {
@Bean
public ExecutorService executorService() {
return Executors.newSingleThreadExecutor();
}
}
}
1 |
@DataJpaTest indicates that Spring test should create repositories are related objects for this test. |
2 |
Since we will use a real PostgreSQL database, Spring should not autoconfigure a test database for us. |
3 |
The JUnit 5 @Tag annotation allows us to group tests in a logical group so we can execute all of them at once. |
4 |
To configure the database, we add an application-data-jpa-test.properties file with the JDBC url, username, password, …
By activating the data-jpa-test profile, Spring will load the properties file automatically. |
5 |
An inner class annotated with @TestConfiguration allows to manually define extra beans our test might need (NOTE: In this case, it is not needed, but I wanted to add this to show how it can be done) |
For completeness, this is the properties file:
spring.datasource.url=jdbc:tc:postgresql:12:///albumdb?TC_TMPFS=/testtmpfs:rw
spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.datasource.username=user
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=validate
We will need to repeat all these annotations for each of our repository tests that we will write in the project.
Clearly, this is not ideal.