适用场景
- 功能开发完成后,后端开发人员进行代码自测
- 代码重构后,集成测试
一些注解
annotations | description |
---|
@Test | 用例 | @ParameterizedTest | 参数化用例 | @DisplayName | 标识用例的名称 | @BeforeEach | 每个用例运行之前操作 | @AfterEach | 每个用例运行之后操作 | @BeforeAll | 本类的每个用例运行之前操作 | @AfterAll | 本类的每个用例运行之前操作 | @Nested | 标识为嵌套用例集合,该标识下,@BeforeAll与AfterAll,仅运行一次 |
Getting Started
场景一
@SpringBootTest
@TestPropertySource("classpath:application-test.properties")
public class ExampleTestApplicationTests {
@Autowired
private UserInfoDao userInfoDao;
@Test
@DisplayName("用例: 用户更新")
public void updateUserInfo() {
UserInfo userInfo = UserInfo.builder().id(1L).userName("香槟").build();
int result = userInfoDao.updateById(userInfo);
Assertions.assertEquals(1,result);
}
}
场景二
- 测试接口异常
- 通过Assertions.assertThrows(…)来断言,获取异常
- JdbcTestUtil2是我针对JdbcTestUtil自己扩展的,用来准备数据、校验数据;这里准备数据与校验数据不直接用dao的原因(测试场景脱离业务代码,防止业务dao语句存在问题而影响测试结果)
@Test
public void registerDuplicate() {
String prepareStatement = "INSERT INTO `user_info` (`id`, `user_name`) VALUES ('101', '香槟')";
int prepareRows = JdbcTestUtil2.updateTable(jdbcTemplate, prepareStatement);
Assertions.assertEquals(1, prepareRows);
DataIntegrityViolationException exception = Assertions.assertThrows(DataIntegrityViolationException.class, () -> {
userInfoService.register("香槟");
});
Throwable rootCause = Optional.ofNullable(ExceptionUtils.getRootCause(exception)).orElse(exception);
Assertions.assertEquals("Duplicate entry '香槟' for key 'idx_uinfo_uname'", rootCause.getMessage());
}
场景三
- 测试下Controller接口
- mockMvc是针对DispatcherServlet进行操作的,这就需要我们手动添加一些filters,这与interrupter不同
@Autowired
private WebApplicationContext webApplicationContext;
public MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilters(webApplicationContext.getBean(LoggerFilter.class))
.build();
@Test
@DisplayName("注册成功")
public void registerSuccess() throws Exception {
MvcResult mvcResult = mockMvc.perform(
MockMvcRequestBuilders.get("/api/user/register")
.header("traceId", "123456")
.param("userName", "香槟"))
.andExpect(MockMvcResultMatchers.status().is2xxSuccessful())
.andReturn();
String responseStr = mvcResult.getResponse().getContentAsString();
BaseResult<Object> baseResult = objectMapper.readValue(responseStr, new TypeReference<BaseResult<Object>>() {
});
Assertions.assertEquals("success", baseResult.getCode());
}
场景四
- 接口入参条件不同,返回的结果是相同的,这时候写多个@Test函数,会造成代码重复性较高,也不利维护
- 引入 @ParameterizedTest 参数化用例,该注解仅适用于同返回结果,Assertions…相同场景
@ParameterizedTest
@CsvSource({
"'99999899',123456",
"'99999899',dfadfdf",
"'99999899',0000000",
})
public void userInfoModify(ArgumentsAccessor arguments) throws Exception {
UserInfoModifyCommand build = UserInfoModifyCommand.builder()
.oldPassword((String) arguments.get(0))
.password((String) arguments.get(1))
.build();
}
场景五
- 用例归类;往往我们一个Controller/Bean的Method有多个场景需要测试,这时候如果在idea-structure里会看到几十个的method,非常不利于阅读
- 引入 @Nested 、内部类
@Nested
@DisplayName("注册")
class registerTest {
@Test
@DisplayName("注册成功")
public void registerSuccess(){
}
@Test
@DisplayName("重复注册")
public void registerDuplicate(){
}
}

场景六
- 每个测试用例的数据需要隔离; 所以我们可以在执行用例前,清一下表数据
- show tables; => TableName.getAllTableNames()
@BeforeEach
public void cleanTable() {
JdbcTestUtils.deleteFromTables(jdbcTemplate, TableName.getAllTableNames());
}
场景七
- 外部渠道、中间件由于存在不稳定性及依赖关系,我们需要对相关的API进行Mock
mockito
@MockBean
private UserDao userDao;
@Test
@DisplayName("用例: 注册成功")
public void registerSuccess() {
Mockito.when(userDao.save(Mockito.any()))
.thenReturn(UserInfo.builder().userName("小西").build());
Mockito.when(userDao.save(Mockito.any())).thenThrow(new IllegalArgumentException("异常"));
}
场景八
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>


|