Тесты репозиториев: @DataJpaTest
Запросы по имени метода и @Query легко сломать опечаткой в условии — приложение запустится, но найдёт не то. Их стоит проверять отдельно. Поднимать ради этого всё приложение с веб-частью и безопасностью незачем. Аннотация @DataJpaTest создаёт срез (slice) — только то, что нужно для работы с базой: сущности, репозитории, Flyway и транзакции.
Тест репозитория
// src/test/java/com/example/blog/PostRepositoryTests.java
package com.example.blog;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Instant;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
@DataJpaTest
class PostRepositoryTests {
@Autowired
private PostRepository posts;
@Autowired
private UserRepository users;
@Test
void findsPostsByTitle() {
User anna = users.save(new User("anna", "{noop}changeme"));
posts.save(new Post("Привет, Spring", "Текст", anna, Instant.now()));
posts.save(new Post("Записи в Java", "Текст", anna, Instant.now()));
List<Post> found = posts.findByTitleContainingIgnoreCase("spring");
assertThat(found).extracting(Post::getTitle).containsExactly("Привет, Spring");
}
@Test
void searchesTitleAndBody() {
User oleg = users.save(new User("oleg", "{noop}changeme"));
posts.save(new Post("Spring Data JPA", "Текст", oleg, Instant.now()));
assertThat(posts.search("data")).hasSize(1);
assertThat(posts.search("ТЕКСТ")).hasSize(1);
assertThat(posts.findByAuthorUsernameOrderByCreatedAtDesc("oleg")).hasSize(1);
assertThat(posts.findLatest(5)).hasSize(1);
}
}extracting(Post::getTitle) берёт у найденных статей заголовки, а containsExactly проверяет, что найден ровно один — нужный. Второй тест проверяет и JPQL-поиск без учёта регистра, и запрос через связь с автором, и обычный SQL с limit.
Что делает @DataJpaTest
| Свойство | Значение |
|---|---|
| Какие бины создаются | Сущности, репозитории, DataSource, Flyway |
| Какие не создаются | Контроллеры, сервисы, безопасность |
| База | Встроенная в память, вместо настроенной в application.yaml |
| Транзакции | Каждый тест откатывается автоматически |
Подмену базы видно в журнале:
INFO ... Replacing 'dataSource' DataSource bean with embedded version
INFO ... Starting embedded database: url='jdbc:h2:mem:8fea3dcb-8214-4168-a703-9500904c0505;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'Профиль test здесь не нужен: база и так своя для каждого запуска. Миграции Flyway применяются к ней так же, как к настоящей, поэтому тест заодно проверяет SQL миграций.
Результат
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.094 s -- in com.example.blog.PostRepositoryTestsСрез поднимается за секунду с небольшим — заметно быстрее полного @SpringBootTest.
Какой тест выбрать
| Что проверяем | Аннотация |
|---|---|
| Запросы репозиториев | @DataJpaTest |
| Адреса, коды ответов, безопасность | @SpringBootTest + @AutoConfigureMockMvc |
| Логику сервиса вместе с базой | @SpringBootTest |
| Класс без Spring | Обычный JUnit без аннотаций Spring |
Частая ошибка
Если повесить @EnableCaching или @EnableScheduling на главный класс с @SpringBootApplication, срез попытается включить и их, но нужных бинов в нём нет:
No qualifying bean of type 'org.springframework.cache.CacheManager' available: no CacheResolver specified - register a CacheManager bean or remove the @EnableCaching annotation from your configuration.Держите такие аннотации в отдельном классе с @Configuration, например в AppConfig. Полное приложение его загрузит, а срез @DataJpaTest пропустит.