Если @SpringBootTest поднимает для теста весь контекст, то @DataJdbcTest — только тот, что относится к Spring Data. В том числе она поднимает наследников CrudRepository — они включаются в контекст.
Вернемся к примеру с составным репозиторием и рассмотрим тесты для него.
@DataJdbcTest
Класс теста AnimalRepositoryTest аннотирован @DataJdbcTest. При запуске теста поднимается частично контекст и запускаются скрипты schema.sql и data.sql. Они добавляют в таблицу animal две строки:
insert into animal (name) values ('cat'); insert into animal (name) values ('dog');
Тест givenAnimal_whenInsert_thenReturnsAnimal добавляет еще одну строку в animal, а тест givenData_whenGetAll_thenCountIsTwo считает количество строк:
@DataJdbcTest class AnimalRepositoryTest { @Autowired private AnimalRepository dao; @Test void givenData_whenGetAll_thenCountIsTwo() { List<Animal> animals = dao.getAll(); Assertions.assertEquals(2, animals.size()); } // пользовательский метод @Test void givenAnimal_whenInsert_thenReturnsAnimal() { Animal animal = dao.insert(new Animal("mouse")); Assertions.assertEquals("mouse", animal.getName()); } //Query method @Test void givenName_whenFindBy_thenReturnsAnimal() { Animal animal = dao.findByName("cat"); Assertions.assertEquals("cat", animal.getName()); } }
Каждый тест выполняется внутри отдельной транзакции, и умолчанию транзакция откатывается после каждого теста.
То есть несмотря на добавление записи в givenAnimal_whenInsert_thenReturnsAnimal, все тесты проходят. В каком бы порядке тесты ни выполнялись (а порядок выполнения тестов всегда произвольный, мы его выбрать не можем):
Это происходит потому, что внутри @DataJdbcTest содержит аннотацию @Transactional. Это превращает каждый тест в транзакцию, которую можно откатить, что и происходит по умолчанию.
@Commit
Если же транзакции откатывать после каждого теста не нужно, то на уровне класса или конкретного метода можно поставить аннотацию @Commit:
@DataJdbcTest @Commit class AnimalRepositoryTest { //@Test ..наши тесты }
Правда, в этом случае конкретно наш тест не пройдет, поскольку добавление записи не откатится. А значит, подсчет количества строк даст 3, а не 2:
Тут можно вспомнить про жесткий способ исправить ситуацию:
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
Если аннотировать класс таким образом, то контекст будет пересоздаваться после каждого метода. Будет заново подниматься база данных h2, заново будут запущены скрипты. Аннотация ресурсоемкая, так делать не нужно.
Но все же попробуем пересоздавать контекст хотя бы после запуска всего теста, а не каждого метода. А лучше — не после этого теста, а перед другим.
@DirtiesContext
Оставим AnimalRepositoryTest в таком виде, в котором тест не проходит:
@DataJdbcTest @Commit class AnimalRepositoryTest { //@Test ..наши тесты }
И скопируем класс в AnimalRepositoryTest1, чтобы выполнять не один, а какие-нибудь два класса-теста.
В скопированном классе уберем @Commit:
@DataJdbcTest class AnimalRepositoryTest1 { @Autowired private AnimalRepository dao; @Test void givenData_whenGetAll_thenCountIsTwo() { List<Animal> animals = dao.getAll(); Assertions.assertEquals(2, animals.size()); } // пользовательский метод @Test void givenAnimal_whenInsert_thenReturnsAnimal() { Animal animal = dao.insert(new Animal("mouse")); Assertions.assertEquals("mouse", animal.getName()); } //Query method @Test void givenName_whenFindBy_thenReturnsAnimal() { Animal animal = dao.findByName("cat"); Assertions.assertEquals("cat", animal.getName()); } }
Тесты запускаем вместе, для этого нужно:
- щелкнуть правой кнопкой на папке тестов
- выбрать Run Tests
Сейчас несмотря на то, что в AnimalRepositoryTest1 нет аннотации @Commit, то есть транзакции откатываются, тест AnimalRepositoryTest1 не проходит. Все потому, что остался грязный контекст с предыдущего теста — исправленная база с 3 строками вместо двух.
Чтобы пересоздать контекст перед тестами AnimalRepositoryTest1, добавим в него аннотацию:
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
Она говорит: контекст грязный, пересоздай его перед запуском теста, в том числе заново подними h2 и запусти скрипты data.sql:
@DataJdbcTest @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) class AnimalRepositoryTest1 { // @Test ...наши тесты }
Теперь AnimalRepositoryTest1 проходит, хоть AnimalRepositoryTest и подпортил контекст. DirtiesContext.ClassMode.BEFORE_CLASS его исправляет:
Итоги
Исходный код есть на GitHub.
Спасибо! Продолжайте, пожалуйста, писать. Это крайне полезно.