1、idea整合junit5之前,先安装配置junit插件:https://blog.csdn.net/nicolas12/article/details/81223938
2、添加pom依赖:
org.junit.platform
junit-platform-launcher
1.3.2
test
org.junit.jupiter
junit-jupiter-engine
5.3.2
test
org.junit.vintage
junit-vintage-engine
5.3.2
test
org.junit.jupiter
junit-jupiter-params
5.3.2
test
org.junit.jupiter
junit-jupiter-api
5.3.2
test
3、命名:工具生成的名字一般是在测试的方法名前面加test及首字母大写,比如addApps -> testAddApps
但是一般测试不同的条件场景情况较多,所以一般建议不同的入参命名规则为:前面的名字不变,后面加入参的条件,举例为testAddAppsWhenChannelIdIsZero和testAddAppsWhenChannelNotExist就是不同的情况用When连接不同的条件。
4、几种常用的注解(导org.junit.jupiter包)
A、@Test 表示方法是一种测试方法
B、@Disabled 表示会跳过此测试方法
C、@DisplayName 为测试类或者测试方法自定义一个名称,举例
@DisplayName("test Disabled")
@Disabled
@Test
void testDisabled(){
log.info("test Disabled");
}
D、@BeforeEach 表示方法在每个测试方法运行前都会运行
E、@AfterEach 表示方法在每个测试方法运行之后都会运行
F、@BeforeAll 表示方法在所有测试方法之前运行(类级别方法,必须位静态方法)
G、@AfterAll 表示方法在所有测试方法之后运行 (类级别方法,必须位静态方法)
@BeforeAll
public static void BeforeEach() throws Exception {
//在所有测试方法运行前运行
log.info("Run before all test methods run");
}
@BeforeEach
public void before() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
H、@RepeatedTest(重复测试,测试高并发用)
I、@EnabledOnOs(在什么环境执行),如 @EnabledOnOs({ LINUX, MAC }),也可以自定义举例:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Test
@EnabledOnOs(MAC)
@interface TestOnMac {
}
@TestOnMac
void testOnMac() {
// ...
}
J、@EnabledOnJre(基于哪个版本的jre执行)如 @EnabledOnJre({ JAVA_9, JAVA_10 })
5、断言
A、assertEquals 断言预期值和实际值相等(参数可以是int double string等等)
B、assertFalse 断言条件为假
C、assertNotNull 断言不为空
D、assertFalse 断言条件为真 举例:
//参数为int
assertEquals(Math.round(Double.valueOf(gson.fromJson(result, Map.class).get("code").toString())),0);
//参数位字符串
assertEquals(result,"");
//参数为boolean
assertTrue(Math.round(Double.valueOf(gson.fromJson(result, Map.class).get("code").toString()))==0);
E、assumeTrue 假设为true时才会执行,如果为false,那么将会直接停止执行 举例:
assumeTrue(!gson.fromJson(result, Map.class).get("msg").toString().contains("channelId must more than or equal to 1"));
assumeTrue(!gson.fromJson(result, Map.class).get("msg").toString().contains("name must hava a value"));
assumeTrue(!gson.fromJson(result, Map.class).get("msg").toString().contains("appKey must hava a value"));
6、参数化测试
@ParameterizedTest(替代@Test)
@CsvSource(多个参数的多组测试)
@ValueSource(单个参数的多组测试)
@EnumSource 其实跟@ValueSource差不多,只不过可以复用枚举类。举例:
public enum ActivityLimitEnum {
LIMIT(1,"封顶"),
UNLIMIT(0,"上不封顶");
}
@ParameterizedTest
@EnumSource(ActivityLimitEnum.class)
@DisplayName("封顶和不封顶")
void test(ActivityLimitEnum activityLimitEnum) {
if (ActivityLimitEnum.LIMIT.equals(activityLimitEnum)) {
assertFalse(false);
}
else if (ActivityLimitEnum.UNLIMIT.equals(activityLimitEnum)) {
assertTrue(true);
}
}
@MethodSource(将一个方法的返回值作为测试方法的入参,引用的方法返回值必须是Stream, Iterator 或者Iterable) 如:
@ParameterizedTest
@MethodSource("stringGenerator")
public void test(String str){
System.out.println(str);
}
static Stream stringGenerator(){
return Stream.of("hello", "world", "let's", "test");
}
7、MockMvc使用(模拟controller请求接收)
A、GET请求
/**
* Method: findAppListByChannelId(Integer channelId)
*/
@DisplayName("query apps information by channelId")
@ParameterizedTest
@ValueSource(strings = {"1", "2", "3"})
public void testFindAppListByChannelId(String channelId) throws Exception {
String result = mockMvc.perform(
get("/v1/appver/apps/list-by-channel") //请求的url,请求的方法是get
.param("channelId", channelId) //添加参数
.contentType(MediaType.APPLICATION_JSON_UTF8)
).andExpect(status().isOk()) //返回的状态是200
.andReturn().getResponse().getContentAsString(); //将相应的数据转换为字符串
assertTrue(Math.round(Double.valueOf(gson.fromJson(result, Map.class).get("code").toString()))==0);
}
注意:get请求的参数全部写String类型,由param方法自动装配成需要的类型。
B、POST请求
/**
* Method: addApps(AppsBo appsBo)
*/
@DisplayName("add apps information success")
@ParameterizedTest
@CsvSource({"app1, key1,1", "app2, key2,1"})
public void testAddAppsSuccess(String name, String appKey, int channdlId) throws Exception {
log.info("test testAddAppsSuccess end");
Date date = new Date();
AppsBo appsBo = new AppsBo();
appsBo.setName(name);
appsBo.setChannelId(channdlId);
appsBo.setAppKey(appKey);
String requestBody = gson.toJson(appsBo);
Map map = gson.fromJson(requestBody, Map.class);
map.put("createTime", date.getTime());
String newRequestBody = gson.toJson(map);
String result = mockMvc.perform(MockMvcRequestBuilders
.post("/v1/appver/apps/add")
.content(newRequestBody)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn()
.getResponse().getContentAsString();|-----|-----|-----|
//assumeTrue的参数为true时才继续往下走,否则就停止到此
assumeTrue(!gson.fromJson(result, Map.class).get("msg").toString().contains("The channel not exist"));
assertEquals(Math.round(Double.valueOf(gson.fromJson(result, Map.class).get("code").toString())),0);
log.info("test testAddAppsSuccess finished");
}
几个方法的简单说明:
方法名 | 作用 | 举例 |
---|---|---|
param | 请求的参数设置方法 | .param(“channelId”, channelId) |
post | 请求方法 | .post("/v1/appver/apps/add") |
content | 请求参数 | .content(newRequestBody) |
contentType | 参数类型 | .contentType(MediaType.APPLICATION_JSON) |
accept | 接收返回值类型 | .accept(MediaType.APPLICATION_JSON) |
andExpect | 添加执行完成后的断言 | .andExpect(status().isOk()) |
andReturn | 表示执行完成后返回相应的结果 | .andReturn() |
最后注意:因为测试不能影响数据库的数据,所以测试类上都要加事务回滚 如下:
@Slf4j
@SpringBootTest
@ExtendWith(SpringExtension.class)
@Rollback
@Transactional
public class AppverAppsControllerTest {
测试用例得出的某些问题:
1、添加数据时,要考虑重名判断,用assumeTrue判断返回的校验提示语。
2、修改数据时,要考虑参数Id是否在表中存在,不存时候mybatis-plus会将其作为插入数据处理,插入数据时要保证某些数据不能为空,所以建议加个自定义校验,用assumeTrue判断返回的校验提示语,专门处理junit或者postMan请求的情况。也要和添加数据一样考虑各种校验问题。