Junit 单元测试

Junit 单元测试学习

POM 依赖

在使用 IDEA 新建 SpringBoot 项目时一般会自动引入此依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

测试类基类

测试类文件夹和项目的 main 文件夹在同一层级
测试类存放的位置和项目的 Application 类在同一相对目录下
自动生成的测试类名称为 XXXApplicationTests
在这个类下补齐所需要的注解,之后的测试类直接继承该类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RunWith(SpringRunner.class)
@SpringBootTest(classes = xxxApplication.class)
@WebAppConfiguration
public class xxxApplicationTests {

@Before
public void init() {
System.out.println("开始测试-----------------");
}

@After
public void after() {
System.out.println("测试结束-----------------");
}
}

测试类

测试类继承测试基类
通过 @Autowired 引入想要测试的类 DAO, Service ……

测试方法

在真正的测试方法前要加上 @Test 注解
加上 @Transactional 后方法会回滚,不会在数据库中产生脏数据
如果想要跳过其中一个测试方法,在方法前加上 @Ignore(message) 即可忽略该方法

Service 单元测试

1
2
3
4
5
/**
* value 是想要测试的变量值
* matcher statement 接收 value 并判断是否符合条件
*/
Assert.assertThat([value], [matcher statement]);

Assert 断言

字符相关匹配符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* equalTo匹配符断言被测的testedValue等于expectedValue,
* equalTo可以断言数值之间,字符串之间和对象之间是否相等,相当于Object的equals方法
*/
assertThat(testedValue, equalTo(expectedValue));
/**
* equalToIgnoringCase匹配符断言被测的字符串testedString
* 在忽略大小写的情况下等于expectedString
*/
assertThat(testedString, equalToIgnoringCase(expectedString));
/**
* equalToIgnoringWhiteSpace匹配符断言被测的字符串testedString
* 在忽略头尾的任意个空格的情况下等于expectedString,
* 注意:字符串中的空格不能被忽略
*/
assertThat(testedString, equalToIgnoringWhiteSpace(expectedString);
/**containsString匹配符断言被测的字符串testedString包含子字符串subString**/
assertThat(testedString, containsString(subString) );
/**endsWith匹配符断言被测的字符串testedString以子字符串suffix结尾*/
assertThat(testedString, endsWith(suffix));
/**startsWith匹配符断言被测的字符串testedString以子字符串prefix开始*/
assertThat(testedString, startsWith(prefix));
一般匹配符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**nullValue()匹配符断言被测object的值为null*/
assertThat(object,nullValue());
/**notNullValue()匹配符断言被测object的值不为null*/
assertThat(object,notNullValue());
/**is匹配符断言被测的object等于后面给出匹配表达式*/
assertThat(testedString, is(equalTo(expectedValue)));
/**is匹配符简写应用之一,is(equalTo(x))的简写,断言testedValue等于expectedValue*/
assertThat(testedValue, is(expectedValue));
/**
* is匹配符简写应用之二,is(instanceOf(SomeClass.class))的简写,
* 断言testedObject为Cheddar的实例
*/
assertThat(testedObject, is(Cheddar.class));
/**not匹配符和is匹配符正好相反,断言被测的object不等于后面给出的object*/
assertThat(testedString, not(expectedString));
/**allOf匹配符断言符合所有条件,相当于“与”(&&)*/
assertThat(testedNumber, allOf( greaterThan(8), lessThan(16) ) );
/**anyOf匹配符断言符合条件之一,相当于“或”(||)*/
assertThat(testedNumber, anyOf( greaterThan(16), lessThan(8) ) );
数值相关匹配符
1
2
3
4
5
6
7
8
9
10
/**closeTo匹配符断言被测的浮点型数testedDouble在20.0¡À0.5范围之内*/
assertThat(testedDouble, closeTo( 20.0, 0.5 ));
/**greaterThan匹配符断言被测的数值testedNumber大于16.0*/
assertThat(testedNumber, greaterThan(16.0));
/** lessThan匹配符断言被测的数值testedNumber小于16.0*/
assertThat(testedNumber, lessThan (16.0));
/** greaterThanOrEqualTo匹配符断言被测的数值testedNumber大于等于16.0*/
assertThat(testedNumber, greaterThanOrEqualTo (16.0));
/** lessThanOrEqualTo匹配符断言被测的testedNumber小于等于16.0*/
assertThat(testedNumber, lessThanOrEqualTo (16.0));
集合相关匹配符
1
2
3
4
5
6
7
8
/**hasEntry匹配符断言被测的Map对象mapObject含有一个键值为"key"对应元素值为"value"的Entry项*/
assertThat(mapObject, hasEntry("key", "value" ) );
/**hasItem匹配符表明被测的迭代对象iterableObject含有元素element项则测试通过*/
assertThat(iterableObject, hasItem (element));
/** hasKey匹配符断言被测的Map对象mapObject含有键值“key”*/
assertThat(mapObject, hasKey ("key"));
/** hasValue匹配符断言被测的Map对象mapObject含有元素值value*/
assertThat(mapObject, hasValue(value));

Controller 单元测试

会用到 MockMvc,MockMvc 实现了对 HTTP 请求的模拟,可以不用启动工程来测试接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@RunWith(SpringRunner.class)
@SpringBootTest
public class xxxControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mvc;
private MockHttpSession session;

/**
* 使用 MockMvc 时要先使用 MockMvcBuilders 构建 MockMvc 对象
* 加入 session 部分代码时因为拦截器那边会判断用户是否登录,所以注入一个用户
* 如果修改了拦截器取消验证,可以不需要该部分代码
*/
@Before
public void setupMockMvc(){
// 初始化MockMvc对象
mvc = MockMvcBuilders.webAppContextSetup(wac).build();
session = new MockHttpSession();
User user =new User("root","root");
session.setAttribute("user",user);
}

/**
* GET方法测试用例
* mockMvc.perform 负责执行一个请求
* MockMvcRequestBuilders.get("/path") 构造一个请求,根据请求类型调用相应的方法
* contentType(MediaType.APPLICATION_JSON_UTF8) 代表发送端的数据格式为 application/json;charset=UTF-8
* accept(MediaType.APPLICATION_JSON_UTF8) 代表客户端希望接受的数据格式为 accept(MediaType.APPLICATION_JSON_UTF8)
* session.(session) 注入一个 session 来通过拦截器
* ResultActions.andExpect 添加执行完成后的断言
* ResultActions.andExpect(MockMvcResultMatchers.status().isOk()) 查看请求返回的状态码是否为 200,如果不是,抛出异常,测试不通过
* ResultActions.andExpect(MockMvcResultMatchers.jsonPath("$.key1").value("value1")) 查看是否得到预期的返回值,如果不是,测试不通过
* ResultActions.andDo() 添加一个结果处理器, 比如 MockMvcResultHandlers.print() 输出整个响应结果信息
* @throws Exception
*/
@Test
public void getTest() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/path")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.session(session)
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.key1").value("value1"))
.andExpect(MockMvcResultMatchers.jsonPath("$.key2").value("value2"))
.andDo(MockMvcResultHandlers.print());
}
}

打包测试

用一个 TestSuits 类整理所有测试类然后打包执行测试方法

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Created by simba
* Date 2021/2/2
* Description:打包测试
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({xxxTest.class,xxxTest2.class})
public class TestSuits {

//不用写代码,只需要注解即可

}

参考

https://blog.csdn.net/weixin_39800144/article/details/79241620
http://tengj.top/2017/12/28/springboot12/#Service单元测试