Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Тесты репозиториев: @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 пропустит.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.