Тестирование репозиториев: @DataJdbcTest, @Commit, @DirtiesContext

Если @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:

С @Commit тест не проходит
Если поставить @Commit, то тест не проходит

Тут можно вспомнить про жесткий способ исправить ситуацию:

@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 строками вместо двух.

В первом классе @Commit, во втором - нет
В первом классе @Commit, во втором — нет

Чтобы пересоздать контекст перед тестами 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 его исправляет:

@Commit в первом тесте, @DirtiesContext перед вторым
@Commit в первом классе, @DirtiesContext перед вторым

Итоги

Исходный код есть на GitHub.

Тестирование репозиториев: @DataJdbcTest, @Commit, @DirtiesContext: 1 комментарий

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *