Несколько дней новогоднего волшебства:
Успейте начать обучение в 2018-ом году со скидкой до 30%!
Выбрать курс

Тестирование вместе с Spring Boot. Часть 2

Spring_Deep_21.11_site-5020-ac4119.png

В прошлой части мы рассмотрели тестирование слоя контроллера, в этой части давайте рассмотрим поближе тестирование бизнес-слоя и слоя JPA-репозиториев.

К примеру у нас есть следующий код:

// ru.otus.example.Main
@EnableJpaRepositories
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }
}

// ru.otus.example.jpa.PersonRepository
public interface PersonRepository extends JpaRepository<Person, Integer> {
}

// ru.otus.example.service.PersonServiceImpl
public class PersonServiceImpl implements PersonService {
    private final PersonRepository repository;   

    @Autowired
    public PersonServiceImpl(PersonRepository repository) {
        this.repository = repository;
    }

    @Override
    public Person getById(int id) {
        repository.getOne(id);
    }

    // И ещё, конечно, какие-то методы, и их много
}

В данном примере у нас имеется SpringBoot-приложение с JPA-репозиторием и бизнес-сервисом, который работает, в частности, с репозиторием.

Чтобы протестировать сервис (бизнес-слой) в принципе не нужен SpringBoot — всегда можно создать моки (в данном случае PersonRepository), и если класс написан в соответствии с IoC, то нам вполне этого достаточно.

А можем всё-таки использовать аннотацию @SpringBootTest. Зачем, станет понятно чуть позже.

Итак, тест выглядит следующим образом:

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTests {

    @MockBean
    private PersonRepository personRepository;
    @Autowired
    private PersonService personService


    @Test
    public void testGetById() {
        given(this.personRepository.getById(any()))
            .willReturn(new Person(42, "Ivan"));
        Person person = personService.getById(42);
        assertThat(person.getId()).isEqualTo(42);
        // и какие-нибудь другие проверки
    }
}

Разберёмся немного, как работает аннотация SpringBootTest: 1. Тест c @SpringBootTest находится в пакете ru.otus.example.service. Движок идёт по пакету вверх, пока не встретит класс, помеченный аннотацией @SpringBootConfiguration (как ни странно, это наш Main, потому что аннотация @SpringBootApplication помечена искомой). 2. Далее происходит поиск бинов, сервисов и других конфигураций согласно @ComponentScan (да, эта аннотация тоже включена в @SpringBootApplication), спускаясь от этого класса. 3. Но не все бины будут созданы для теста. Тест содержит поле PersonRepository, помеченное @MockBean. Поэтому вместо реального репозитория, в PersonService придёт мок. 4. Ну и после поднятия контекста в @Autowired придёт созданный сервис.

Подобная аннотация SpringBootTest позволяет также писать интеграционные тесты. В данном примере будет использоваться уже реальный JPA-репозиторий.

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTests {
    @Autowired
    private PersonService personService


    @Test
    public void testGetByIdFromDb() {
        Person person = personService.getById(42); // уже из базы
        assertThat(person.getId()).isEqualTo(42);
        // и какие-нибудь другие проверки
    }
}

Такие тесты используют настроенную в application.yml базу данных. В принципе, JPA-репозитории можно тестировать на embedded DB без особых проблем. Конечно, использование нативных SQL-скриптов и хранимых процедур сказывается на возможности и смысле такого тестирования. Но если вы используете только чистый JPA и, например, liquibase в формате YAML, то корректность маппингов проверить можно, и даже нужно — это позволит избежать ошибок на реальной БД.

И выглядит тест на JPA вот таким образом:

@RunWith(SpringRunner.class)
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class PersonRepositoryTest {
    @Autowired
    private PersonRepository personRepository;

    // вот такой тест позволит выявить ошибки в маппинге
    // тестировать другие методы в репозитории по большей части не нужно
    @Test
    public void testSaveAndGet() {
        personRepository.getById(new Person(42, "Ivan"));
        Person fromDb = personRepository.getOne(42);
        assertThat(fromDb.getId).isEqualTo(42);
        // и какие-нибудь другие проверки на поля
    }
}
.

На этом всё. Оставим читателю возможность самому изучить, всё-таки, на какой БД это будет выполнено!

Есть вопрос? Пишите комментарий!

Автор
0 комментариев
Для комментирования необходимо авторизоваться