1. Обзор
В этом учебнике, мы будем смотреть на написание тестов с использованием поддержки фреймворка в Spring Boot. Мы будем охватывать единицы тестов, которые могут работать в изоляции, а также интеграционные тесты, которые будут bootstrap Весна контексте перед выполнением тестов.
Если вы нов к загрузке весны, то заканчивайте вне наше интро к загрузке весны.
Дальнейшее чтение:
Изучение весенней загрузки TestRestTemplate
Быстрый путеводитель по @RestClientTest в весенней загрузки
Инъекционные Mockito Мокс в весенние бобы
2. Настройка проекта
Приложение, которое мы собираемся использовать в этой статье, является API, который предоставляет некоторые основные операции на Сотрудник ресурс. Это типичная многоуровневая архитектура – вызов API обрабатывается с Контроллер Сервисная в Настойчивость слой.
3. Maven зависимостей
Давайте сначала добавим наши зависимости тестирования:
org.springframework.boot spring-boot-starter-test test 2.2.6.RELEASE com.h2database h2 test
весна-загрузка-стартер-тест является основной зависимостью, которая содержит большинство элементов, необходимых для наших тестов.
H2 DB является нашей базой данных памяти. Это устраняет необходимость настройки и запуска фактической базы данных для тестовых целей.
3.1 JUnit 4
По мере того как загрузка весны 2.4, двигатель JUnit 5 vintage был удален от весна-загрузка-стартер-тест . Если мы все еще хотим писать тесты с помощью JUnit 4, нам нужно добавить следующую зависимость Maven:
org.junit.vintage junit-vintage-engine test org.hamcrest hamcrest-core
4. Интеграционное тестирование с @SpringBootTest
Как следует из названия, интеграционные тесты сосредоточены на интеграции различных уровней приложения. Это также означает, что никаких насмешек не участвует.
В идеале мы должны держать интеграционные тесты отдельно от унитарных тестов и не должны работать вместе с унитарными тестами. Мы можем сделать это, используя другой профиль только для запуска интеграционных тестов. Несколько причин для этого могут быть в том, что интеграционные тесты ото всех времени и, возможно, потребуется фактическая база данных для выполнения.
Однако в этой статье мы не будем фокусироваться на этом, и вместо этого мы будем использовать хранилище сохранения H2 в памяти.
Интеграционные тесты должны выполнить контейнер для выполнения тестовых случаев. Таким образом, некоторые дополнительные установки требуется для этого – все это легко в весенней загрузки:
@RunWith(SpringRunner.class) @SpringBootTest( SpringBootTest.WebEnvironment.MOCK, classes = Application.class) @AutoConfigureMockMvc @TestPropertySource( locations = "classpath:application-integrationtest.properties") public class EmployeeRestControllerIntegrationTest { @Autowired private MockMvc mvc; @Autowired private EmployeeRepository repository; // write test cases here }
@SpringBootTest аннотация полезна, когда нам нужно, чтобы bootstrap весь контейнер. Аннотация работает, создавая ПриложениеКонтекст которые будут использованы в наших тестах.
Мы можем использовать webEnvironment атрибут @SpringBootTest настроить нашу среду времени выполнения; мы используем WebEnvironment.MOCK здесь, так что контейнер будет работать в макет сервлет окружающей среды.
Далее, @TestPropertySource аннотация помогает настроить расположение файлов свойств, специфичных для наших тестов. Обратите внимание, что файл свойств загружается с @TestPropertySource перекроет существующие application.properts файл.
приложение-интеграцияtest.properts содержит сведения для настройки хранилища настойчивости:
spring.datasource.url = jdbc:h2:mem:test spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
Если мы хотим проработать наши интеграционные тесты против MyS’L, мы можем изменить вышеуказанные значения в файле свойств.
Тестовые случаи для интеграционных тестов могут выглядеть как Контроллер услойные единицы испытаний:
@Test public void givenEmployees_whenGetEmployees_thenStatus200() throws Exception { createTestEmployee("bob"); mvc.perform(get("/api/employees") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content() .contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$[0].name", is("bob"))); }
Отличие от Контроллер услойные единицы тестов заключается в том, что здесь ничего не высмеивается и будут выполняться конечные сценарии.
5. Тестовая конфигурация с @TestConfiguration
Как мы видели в предыдущем разделе, тест аннотирован с @SpringBootTest загрузит полный контекст приложения, что означает, что мы можем @Autowire любой боб, который подобрал компонент сканирования в нашем тесте:
@RunWith(SpringRunner.class) @SpringBootTest public class EmployeeServiceImplIntegrationTest { @Autowired private EmployeeService employeeService; // class code ... }
Тем не менее, мы можем избежать загрузки реального контекста приложения, но использовать специальную конфигурацию теста. Мы можем достичь этого с помощью @TestConfiguration аннотация. Существует два способа использования аннотации. Либо на статическом внутреннем классе в том же тестовом классе, где мы хотим @Autowire фасоль:
@RunWith(SpringRunner.class) public class EmployeeServiceImplIntegrationTest { @TestConfiguration static class EmployeeServiceImplTestContextConfiguration { @Bean public EmployeeService employeeService() { return new EmployeeService() { // implement methods }; } } @Autowired private EmployeeService employeeService; }
Кроме того, мы можем создать отдельный класс конфигурации теста:
@TestConfiguration public class EmployeeServiceImplTestContextConfiguration { @Bean public EmployeeService employeeService() { return new EmployeeService() { // implement methods }; } }
Классы конфигурации аннотированы @TestConfiguration исключены из сканирования компонентов, поэтому мы должны импортировать его явно в каждом тесте, где мы хотим @Autowire оно. Мы можем сделать это с @Import аннотация:
@RunWith(SpringRunner.class) @Import(EmployeeServiceImplTestContextConfiguration.class) public class EmployeeServiceImplIntegrationTest { @Autowired private EmployeeService employeeService; // remaining class code }
6. Мокинг с @MockBean
Наша Сервисная слой кода зависит от нашей хранилище:
@Service public class EmployeeServiceImpl implements EmployeeService { @Autowired private EmployeeRepository employeeRepository; @Override public Employee getEmployeeByName(String name) { return employeeRepository.findByName(name); } }
Тем не менее, чтобы проверить Сервисная слой, нам не нужно знать или заботиться о том, как уровень настойчивости реализуется. В идеале, мы должны быть в состоянии писать и тестировать наши Сервисная слой кода без проводки в нашем полном слое настойчивости.
Для достижения этой цели мы можем использовать насмешливый поддержку, предоставляемую Spring Boot Test.
Давайте сначала посмотрим на скелет тестового класса:
@RunWith(SpringRunner.class) public class EmployeeServiceImplIntegrationTest { @TestConfiguration static class EmployeeServiceImplTestContextConfiguration { @Bean public EmployeeService employeeService() { return new EmployeeServiceImpl(); } } @Autowired private EmployeeService employeeService; @MockBean private EmployeeRepository employeeRepository; // write test cases here }
Проверить Сервисная класса, мы должны иметь экземпляр Сервисная класс, созданный и доступный в @Bean так что мы можем @Autowire это в нашем тестовом классе. Мы можем достичь этой конфигурации с помощью @TestConfiguration аннотация.
Еще одна интересная вещь здесь является использование @MockBean . Это создает Mock для СотрудникРеспозиторий , которые могут быть использованы для обхода вызова на фактические СотрудникРеспозиторий :
@Before public void setUp() { Employee alex = new Employee("alex"); Mockito.when(employeeRepository.findByName(alex.getName())) .thenReturn(alex); }
Так как настройка сделана, тестовый случай будет проще:
@Test public void whenValidName_thenEmployeeShouldBeFound() { String name = "alex"; Employee found = employeeService.getEmployeeByName(name); assertThat(found.getName()) .isEqualTo(name); }
7. Интеграционное тестирование с @DataJpaTest
Мы будем работать с организацией под названием Сотрудник, который имеет id и имя по своим свойствам:
@Entity @Table(name = "person") public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Size(min = 3, max = 20) private String name; // standard getters and setters, constructors }
А вот наш репозиторий с использованием весенних данных JPA:
@Repository public interface EmployeeRepository extends JpaRepository{ public Employee findByName(String name); }
Вот и все для кода уровня настойчивости. Теперь давайте напишем наш тестовый класс.
Во-первых, давайте создадим скелет нашего тестового класса:
@RunWith(SpringRunner.class) @DataJpaTest public class EmployeeRepositoryIntegrationTest { @Autowired private TestEntityManager entityManager; @Autowired private EmployeeRepository employeeRepository; // write test cases here }
@RunWith (СпрингРаннер.class) обеспечивает мост между функциями тестирования Spring Boot и JUnit. Всякий раз, когда мы используем какие-либо функции тестирования Spring Boot в наших тестах JUnit, эта аннотация будет необходима.
@DataJpaTest обеспечивает некоторую стандартную настройку, необходимую для тестирования уровня настойчивости:
- настройка базы данных H2 в памяти
- настройка Hibernate, весенних данных и ДанныеИсточник
- выполнение @EntityScan
- включение регистрации S’L
Для выполнения операций DB нам нужны некоторые записи уже в нашей базе данных. Чтобы настроить эти данные, мы можем использовать TestEntityManager.
Весенняя загрузка TestEntityManager является альтернативой стандартной JPA EntityManager который предоставляет методы, обычно используемые при написании тестов.
СотрудникРеспозиторий является компонентом, который мы собираемся проверить.
Теперь давайте напишем наш первый тестовый случай:
@Test public void whenFindByName_thenReturnEmployee() { // given Employee alex = new Employee("alex"); entityManager.persist(alex); entityManager.flush(); // when Employee found = employeeRepository.findByName(alex.getName()); // then assertThat(found.getName()) .isEqualTo(alex.getName()); }
В вышеупомянутом тесте мы используем TestEntityManager вставить Сотрудник в DB и чтение его через найти по имени API.
assertThat (…) часть происходит от Библиотека Assertj , который поставляется в комплекте с весенней загрузки.
8. Удельное тестирование с @WebMvcTest
Наша Контроллер зависит от Сервисная слой; давайте включим только один метод простоты:
@RestController @RequestMapping("/api") public class EmployeeRestController { @Autowired private EmployeeService employeeService; @GetMapping("/employees") public ListgetAllEmployees() { return employeeService.getAllEmployees(); } }
Так как мы сосредоточены только на Контроллер код, это естественно, чтобы издеваться над Сервисная слой кода для наших унитарных тестов:
@RunWith(SpringRunner.class) @WebMvcTest(EmployeeRestController.class) public class EmployeeRestControllerIntegrationTest { @Autowired private MockMvc mvc; @MockBean private EmployeeService service; // write test cases here }
Чтобы проверить Контроллеры , мы можем использовать @WebMvcTest . Он будет автоматически настроить инфраструктуру Spring MVC для наших испытаний единицы.
В большинстве случаев, WebMvcTest будет ограничено bootstrap одного контроллера. Мы также можем использовать его вместе с @MockBean для обеспечения макетных реализаций для любых необходимых зависимостей.
@WebMvcTest также автоматически настраивает МокМвк , который предлагает мощный способ легкого тестирования контроллеров MVC без запуска полного сервера HTTP.
Сказав это, давайте напишем наш тестовый случай:
@Test public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception { Employee alex = new Employee("alex"); ListallEmployees = Arrays.asList(alex); given(service.getAllEmployees()).willReturn(allEmployees); mvc.perform(get("/api/employees") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(1))) .andExpect(jsonPath("$[0].name", is(alex.getName()))); }
получить (…) вызов метода может быть заменен другими методами, соответствующими глаголам HTTP, таким как положить () , пост () и так далее. Обратите внимание, что мы также устанавливаем тип содержимого в запросе.
МокМвк является гибким, и мы можем создать любой запрос, используя его.
9. Авто-настроенные тесты
Одной из удивительных особенностей автоматически настроенных аннотаций Spring Boot является то, что она помогает загружать части полного приложения и тестовые слои кодовой базы.
В дополнение к вышеупомянутым аннотациям, вот список из нескольких широко используемых аннотаций:
- @WebF luxTest : Мы можем использовать @WebFluxTest аннотация для тестирования контроллеров Spring WebFlux. Он часто используется вместе с @MockBean для обеспечения макетных реализаций для требуемых зависимостей.
- @JdbcTest : W e может использовать @JdbcTest аннотация для тестирования приложений JPA, но это для тестов, которые требуют только ДанныеИсточник. Аннотация настраивает встроенную в память базу данных и JdbcTemplate.
- @JooqTest : Для тестирования тестов, связанных с jOO, мы можем использовать @JooqTest аннотация, которая настраивает DSLContext.
- @DataMongoTest : Чтобы протестировать приложения MongoDB, @DataMongoTest является полезной аннотацией. По умолчанию он настраивает встроенный в память MongoDB, если драйвер доступен через зависимости, настраивает МонгоТемплет, сканирование для @Document классов и настраивает репозитории Spring Data MongoDB.
- @DataRedisTest упрощает тестирование приложений Redis. Он сканирует для @RedisHash классы и настраивает репозитории Spring Data Redis по умолчанию.
- @DataLdapTest настраивает встроенный в память LDAP (если таковые имеются), настраивает LdapTemplate , сканирование для @Entry классов и настраивает spring Data LDAP репозитории по умолчанию.
- @RestClientTest : Мы обычно используем @RestClientTest аннотация для тестирования клиентов REST. Он автоматически настраивает различные зависимости, такие как поддержка Джексона, GSON и Jsonb; настраивает RestTemplateBuilder; и добавляет поддержку MockRestServiceServer по умолчанию.
- @JsonTest : Инициализирует контекст приложения Spring только с бобами, необходимыми для тестирования сериализации JSON.
Вы можете прочитать больше об этих аннотациях и о том, как оптимизировать тесты интеграции в нашей статье Оптимизация весенних интеграционных тестов.
10. Заключение
В этой статье мы глубоко погружение в поддержку тестирования в Spring Boot и показали, как писать единицы испытаний эффективно.
Полный исходный код этой статьи может быть найдено на GitHub . Исходный код содержит гораздо больше примеров и различных тестовых случаев.
И если вы хотите продолжать изучать тестирование, у нас есть отдельные статьи, связанные с интеграционными тестами, оптимизацией весенних интеграционных тестов и унитарными тестами в JUnit 5.