предисловие
Springboot предоставляетspirng-boot-starter-test
Чтобы разработчики могли использовать модульные тесты, во введенииspring-boot-starter-test
После зависимости:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Он включает в себя следующие библиотеки:
- Junit — широко используемая библиотека модульного тестирования
- Spring Test и Spring Boot Test — поддержка интеграционного тестирования для приложений Spring
- AssertJ — библиотека утверждений
- Hamcrest — библиотека для сопоставления объектов
- Mockito — фреймворк для имитации Java
- JSONassert — библиотека утверждений для JSON.
- JsonPath — XPath для JSON
Далее мы кратко представим модульный тест с точки зрения уровня сервиса и уровня контроллера.
Тест сервисного модуля
В SpringBoot 2.0 создайте модульный тест для службы со следующим кодом:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest {
@Autowired
private UserService userService;
@Test
public void insertUser() {
User user = new User();
user.setUsername("li ning");
user.setPassword("123456");
userService.insertUser(user);
}
}
Приведенный выше тест очень прост, в основном нужно обратить внимание на две аннотации:@RunWith
и@SpringBootTest
-
@RunWith: этот тег аннотации предоставляется Junit для описания исполнителя этого тестового класса, который используется здесь.
SpringRunner
, который на самом деле наследуетSpringJUnit4ClassRunner
класс, покаSpringJUnit4ClassRunner
Этот класс является настраиваемым расширением для среды выполнения Junit, используемым для стандартизации тестовых случаев Junit4.x в среде Springboot. - @SpringBootTestСоздание контекста для SpringApplication и поддержка функций SpringBoot.
использовать@SpringBootTest
изwebEnvironment
Свойства определяют среду выполнения:
- Макет (по умолчанию): Загрузите WebApplicationContext и предоставьте имитацию веб-среды. Среда сервлета. При использовании этой аннотации встроенный сервер не будет запущен.
- RANDOM_PORT: загрузить WebServerApplicationContext и предоставить реальную веб-среду, встроенный сервер,Порт прослушивания является случайным
- DEFINED_PORT: загрузить WebServerApplicationContext и предоставить реальную веб-среду, встроенный сервер запускается и прослушивает определенный порт (из application.properties или порт по умолчанию 8080).
- NONE: загружает ApplicationContext с помощью SpringApplication, но не предоставляет никакого веб-контекста.
Модульное тестирование контроллера
Сначала создайте контроллер со следующим кодом:
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/user")
public String userMapping(@RequestBody User user){
userService.insertUser(user);
return "ok";
}
}
Затем создайте модульный тест контроллера, обычно существует два метода создания.
Первый — использовать смоделированную среду для тестирования.
По умолчанию @SpringBootTest не запускает сервер.Если вам нужно протестировать веб-конечные точки в этой фиктивной среде, вы можете настроить MockMvc следующим образом:
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void userMapping() throws Exception {
String content = "{\"username\":\"pj_mike\",\"password\":\"123456\"}";
mockMvc.perform(MockMvcRequestBuilders.request(HttpMethod.POST, "/user")
.contentType("application/json").content(content))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("ok"));
}
}
вот один@AutoConfigureMockMvcАннотация, указывающая на то, что он автоматически вводится при запуске теста.MockMvc, и этоMockMvcСуществует несколько основных методов:
-
perform
: выполнить запрос RequestBuilder, который автоматически выполнит процесс SpringMVC и сопоставит его с соответствующим контроллером для обработки. -
andExpect
: добавьте правило проверки RequsetMatcher, чтобы убедиться, что результат правильный после выполнения контроллера. -
andDo
: добавить обработчик результатов ResultHandler, например вывод результата на консоль при отладке. -
andReturn
: Наконец, верните соответствующий MvcResult, а затем выполните пользовательскую проверку/выполните следующую асинхронную обработку.
Вот небольшая хитрость.Вообще говоря, для контроллера часто требуется протестировать более одного запроса Request.Попасть в MockMvcRequestBuilders и MockMvcResultMatchers будет громоздко.Проще всего использовать методы этих двух классов.
import static
Статический импорт, а затем вы можете напрямую использовать статические методы двух классов. Тогда код становится таким:
...
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void userMapping() throws Exception {
String content = "{\"username\":\"pj_mike\",\"password\":\"123456\"}";
mockMvc.perform(request(HttpMethod.POST, "/user")
.contentType("application/json").content(content))
.andExpect(status().isOk())
.andExpect(content().string("ok"));
}
}
Кроме того, если вы просто хотите сосредоточиться на веб-слое вместо запуска полного ApplicationContext, рассмотрите возможность использования@WebMvcTestАннотация, эту аннотацию нельзя использовать с @SpringBootTest, и она ориентирована только на веб-уровень. Что касается уровня данных, необходимо ввести связанные зависимости. Для получения дополнительной информации об этой аннотации, пожалуйста, обратитесь к официальной документации:docs.spring.IO/весенняя загрузка…
Создание объектов MockMvc с помощью MockMvcBuilder.
В дополнение к описанному выше методу прямого внедрения MockMvc с аннотацией @AutoConfigureMockMvc мы также можем использовать MockMvcBuilder для создания объектов MockMvc Пример кода выглядит следующим образом:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest4 {
@Autowired
private WebApplicationContext web;
private MockMvc mockMvc;
@Before
public void setupMockMvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(web).build();
}
@Test
public void userMapping() throws Exception {
String content = "{\"username\":\"pj_m\",\"password\":\"123456\"}";
mockMvc.perform(request(HttpMethod.POST, "/user")
.contentType("application/json").content(content))
.andExpect(status().isOk())
.andExpect(content().string("ok"));
}
}
Второй тест с использованием реальной веб-среды
Установите свойства в аннотации @SpringBootTestwebEnvironment = WebEnvironment.RANDOM_PORT
, доступный порт будет выбираться случайным образом при каждом запуске. Мы также можем использовать@LoalServerPort
Аннотация используется для локального номера порта. Вот тестовый код:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest3 {
@Autowired
private TestRestTemplate testRestTemplate;
@Test
public void userMapping() throws Exception {
User user = new User();
user.setUsername("pj_pj");
user.setPassword("123456");
ResponseEntity<String> responseEntity = testRestTemplate.postForEntity("/user", user, String.class);
System.out.println("Result: "+responseEntity.getBody());
System.out.println("状态码: "+responseEntity.getStatusCodeValue());
}
}
В приведенном выше коде есть ключевой класс -TestRestTemplate, TestRestTemplate является альтернативой Spring RestTemplate, который можно использовать для интеграционного тестирования. Он аналогичен использованию функциональных методов RestTemplate. Он обычно используется при тестировании реальной веб-среды. Более подробное использование этого класса см. в официальной документации. :docs.spring.IO/весенняя загрузка…
откат модульного теста
При модульном тестировании, если вы не хотите создавать мусорные данные, вы можете включить функцию транзакции и добавить ее в заголовок метода или класса.@Transactional
Достаточно аннотации, и это также объяснено в официальной документации:
If your test is @Transactional, it rolls back the transaction at the end of each test method by default. However, as using this arrangement with either RANDOM_PORT or DEFINED_PORT implicitly provides a real servlet environment, the HTTP client and server run in separate threads and, thus, in separate transactions. Any transaction initiated on the server does not roll back in this case
Прочтите его и используйте в модульных тестах@Transactional
Обратите внимание, что по умолчанию транзакция откатывается в конце метода тестирования. Однако есть некоторые особые случаи, о которых следует помнить, когда мы используемRANDOM_PORT
илиDEFINED_PORT
Такая компоновка неявно обеспечивает настоящую среду сервлета, поэтому HTTP-клиент и сервер будут работать в разных потоках, тем самым разделяя транзакции, и в этом случае любые транзакции, запущенные на сервере, не будут откатываться.
Конечно, если вы хотите отключить откат, просто добавьте@Rollback(false)
Просто комментируйте,@Rollback
Указывает, что транзакция откатывается после выполнения, и значение может быть передано. Значение по умолчанию — true для отката и false для отказа от отката.
Есть еще одна ситуация, на которую следует обратить внимание: если вы используете базу данных MySQL, иногда вы обнаружите, что добавляются аннотации.@Transactionl
Откатиться не получится, тогда нужно проверить, является ли ваш движок по умолчанию InnoDB, если нет, смените на InnoDB.
MyISAM и InnoDB — два наиболее часто используемых механизма баз данных для mysql в настоящее время.Основное различие между MyISAM и InnoDB заключается в производительности и управлении транзакциями.Вот краткое введение в разницу и метод преобразования между ними:
- MyISAM: MyISAM является механизмом хранения базы данных по умолчанию до MySQL 5.5.MyISAM обеспечивает высокоскоростное хранение и поиск, а также возможности полнотекстового поиска, подходящие для таких приложений, как хранилища данных с частыми запросами, ноТранзакции и внешние ключи не поддерживаются, и данные невозможно восстановить после повреждения таблицы.
- InnoDB: InnoDB — это механизм хранения базы данных по умолчанию в MySQL 5.5, InnoDB обеспечивает безопасность транзакций с возможностью фиксации, отката и восстановления после сбоя.Поддержка транзакций и внешних ключей, по сравнению с MyISAM, записи InnoDB менее эффективны и занимают больше места на диске для хранения данных и индексов.
Если ваша таблица данных представляет собой механизм MyISAM, поскольку он не поддерживает транзакции, добавьте аннотации транзакций в модульные тесты, и метод тестирования не будет откатываться.
Изменить двигатель по умолчанию
- Просмотр текущего механизма хранения MySQL по умолчанию
mysql> show variables like '%storage_engine%';
- Посмотрите, какой движок используется в конкретной пользовательской таблице таблицы (движок за движком представляет механизм хранения текущей таблицы)
mysql> show create table user;
- Измените пользовательскую таблицу как механизм хранения InnoDB.
mysql> ALTER TABLE user ENGINE=INNODB;
Уведомление
Здесь следует отметить еще одну вещь,Когда мы используем Spring Data JPA, если механизм хранения для создания таблицы MySQL не указан, MySQL MyISAM будет использоваться по умолчанию., это тоже яма, в этом случае вы используете юнит-тест@Transactional
Обратите внимание, что откат не работает.
Решениебудетhibernate.dialect
свойства настроены какhibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
, Укажите, что MySQL использует механизм InnoDB при построении таблицы.Пример файла конфигурации выглядит следующим образом:
spring:
jpa:
# 数据库类型
database: mysql
# 输出日志
show-sql: true
properties:
hibernate:
# JPA配置
hbm2ddl.auto: update
# mysql存储类型配置
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
резюме
Вышеприведенное кратко описывает, как использовать модульное тестирование под Springboot, Для более подробного ознакомления с модульным тестированием обратитесь к официальной документации:docs.spring.IO/весенняя загрузка…