HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Теги: liquibase, spring, тестирование, @componentscan, @autowired, personrepository, spring boot, тестирование jpa-репозиториев, ioc, @springboottest, ru.otus.example.service, @springbootconfiguration, @springbootapplication, @mockbean, application.yml, embedded db, yaml</p>
1 <p>Теги: liquibase, spring, тестирование, @componentscan, @autowired, personrepository, spring boot, тестирование jpa-репозиториев, ioc, @springboottest, ru.otus.example.service, @springbootconfiguration, @springbootapplication, @mockbean, application.yml, embedded db, yaml</p>
2 <p>В<a>прошлой части</a>мы рассмотрели<strong>тестирование слоя контроллера</strong>, в этой части давайте рассмотрим поближе тестирование бизнес-слоя и слоя JPA-репозиториев.</p>
2 <p>В<a>прошлой части</a>мы рассмотрели<strong>тестирование слоя контроллера</strong>, в этой части давайте рассмотрим поближе тестирование бизнес-слоя и слоя JPA-репозиториев.</p>
3 <h2>К примеру у нас есть следующий код:</h2>
3 <h2>К примеру у нас есть следующий код:</h2>
4 // 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&lt;Person, Integer&gt; { } // 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); } // И ещё, конечно, какие-то методы, и их много }<p>В данном примере у нас имеется<strong>SpringBoot</strong>-приложение с JPA-репозиторием и бизнес-сервисом, который работает, в частности, с репозиторием.</p>
4 // 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&lt;Person, Integer&gt; { } // 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); } // И ещё, конечно, какие-то методы, и их много }<p>В данном примере у нас имеется<strong>SpringBoot</strong>-приложение с JPA-репозиторием и бизнес-сервисом, который работает, в частности, с репозиторием.</p>
5 <p>Чтобы протестировать сервис (бизнес-слой) в принципе не нужен<strong>SpringBoot</strong>- всегда можно создать моки (в данном случае<strong>PersonRepository</strong>), и если класс написан в соответствии с<strong>IoC</strong>, то нам вполне этого достаточно.</p>
5 <p>Чтобы протестировать сервис (бизнес-слой) в принципе не нужен<strong>SpringBoot</strong>- всегда можно создать моки (в данном случае<strong>PersonRepository</strong>), и если класс написан в соответствии с<strong>IoC</strong>, то нам вполне этого достаточно.</p>
6 <p>А можем всё-таки использовать аннотацию @SpringBootTest. Зачем, станет понятно чуть позже.</p>
6 <p>А можем всё-таки использовать аннотацию @SpringBootTest. Зачем, станет понятно чуть позже.</p>
7 <h2>Итак, тест выглядит следующим образом:</h2>
7 <h2>Итак, тест выглядит следующим образом:</h2>
8 @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); // и какие-нибудь другие проверки } }<p>Разберёмся немного, как работает аннотация<strong>SpringBootTest</strong>: 1. Тест c @SpringBootTest находится в пакете<strong>ru.otus.example.service</strong>. Движок идёт по пакету вверх, пока не встретит класс, помеченный аннотацией @SpringBootConfiguration (как ни странно, это наш<strong>Main</strong>, потому что аннотация @SpringBootApplication помечена искомой). 2. Далее происходит поиск бинов, сервисов и других конфигураций согласно @ComponentScan (да, эта аннотация тоже включена в @SpringBootApplication), спускаясь от этого класса. 3. Но не все бины будут созданы для теста. Тест содержит поле<strong>PersonRepository</strong>, помеченное @MockBean. Поэтому вместо реального репозитория, в<strong>PersonService</strong>придёт мок. 4. Ну и после поднятия контекста в @Autowired придёт созданный сервис.</p>
8 @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); // и какие-нибудь другие проверки } }<p>Разберёмся немного, как работает аннотация<strong>SpringBootTest</strong>: 1. Тест c @SpringBootTest находится в пакете<strong>ru.otus.example.service</strong>. Движок идёт по пакету вверх, пока не встретит класс, помеченный аннотацией @SpringBootConfiguration (как ни странно, это наш<strong>Main</strong>, потому что аннотация @SpringBootApplication помечена искомой). 2. Далее происходит поиск бинов, сервисов и других конфигураций согласно @ComponentScan (да, эта аннотация тоже включена в @SpringBootApplication), спускаясь от этого класса. 3. Но не все бины будут созданы для теста. Тест содержит поле<strong>PersonRepository</strong>, помеченное @MockBean. Поэтому вместо реального репозитория, в<strong>PersonService</strong>придёт мок. 4. Ну и после поднятия контекста в @Autowired придёт созданный сервис.</p>
9 <p>Подобная аннотация<strong>SpringBootTest</strong>позволяет также писать интеграционные тесты. В данном примере будет использоваться уже реальный<strong>JPA-репозиторий</strong>.</p>
9 <p>Подобная аннотация<strong>SpringBootTest</strong>позволяет также писать интеграционные тесты. В данном примере будет использоваться уже реальный<strong>JPA-репозиторий</strong>.</p>
10 @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); // и какие-нибудь другие проверки } }<p>Такие тесты используют настроенную в<strong>application.yml</strong>базу данных. В принципе, JPA-репозитории можно тестировать на<strong>embedded DB</strong>без особых проблем. Конечно, использование нативных SQL-скриптов и хранимых процедур сказывается на возможности и смысле такого тестирования. Но если вы используете только чистый JPA и, например,<strong>liquibase</strong>в формате<strong>YAML</strong>, то корректность маппингов проверить можно, и даже нужно - это позволит избежать ошибок на реальной БД.</p>
10 @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); // и какие-нибудь другие проверки } }<p>Такие тесты используют настроенную в<strong>application.yml</strong>базу данных. В принципе, JPA-репозитории можно тестировать на<strong>embedded DB</strong>без особых проблем. Конечно, использование нативных SQL-скриптов и хранимых процедур сказывается на возможности и смысле такого тестирования. Но если вы используете только чистый JPA и, например,<strong>liquibase</strong>в формате<strong>YAML</strong>, то корректность маппингов проверить можно, и даже нужно - это позволит избежать ошибок на реальной БД.</p>
11 <p>И выглядит тест на JPA вот таким образом:</p>
11 <p>И выглядит тест на JPA вот таким образом:</p>
12 @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); // и какие-нибудь другие проверки на поля } }<p>На этом всё. Оставим читателю возможность самому изучить, всё-таки, на какой БД это будет выполнено!</p>
12 @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); // и какие-нибудь другие проверки на поля } }<p>На этом всё. Оставим читателю возможность самому изучить, всё-таки, на какой БД это будет выполнено!</p>
13 <p><em>Есть вопрос? Пишите комментарий!</em></p>
13 <p><em>Есть вопрос? Пишите комментарий!</em></p>
14  
14