Spring Boot学习笔记2:使用Junit4单元测试
- 添加单元测试依赖
- 基本单元测试
- 基本注解
- Assert
- Dao层的单元测试
- Service层的单元测试
- Controller层的单元测试
- GitHub示例
添加单元测试依赖
Spring Boot中引入单元测试很简单,依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Spring Boot 2.1.3.RELEASE,引入spring-boot-starter-test后,有如下几个库:
- JUnit: The de-facto standard for unit testing Java applications.
- Spring Test & Spring Boot Test: Utilities and integration test
- support for Spring Boot applications. AssertJ: A fluent assertion
- library. Hamcrest: A library of matcher objects (also known as
- constraints or predicates). Mockito: A Java mocking framework.
- JSONassert: An assertion library for JSON. JsonPath: XPath for JSON.
基本单元测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class Test {
@Test
public void test() {
Assert.assertEquals("O大叔Tz","O大叔Tz");
}
}
基本注解
属性 | 功能 |
| 在JUnit中有很多个Runner,他们负责调用你的测试代码,每一个Runner都有各自的特殊功能,你要根据需要选择不同的Runner来运行你的测试代码。 需要注意:如果我们使用的是JUnit 4 ,那么需要添加 |
| 用于SpringBoot应用测试,默认根据包名逐级往上查找直到找到 |
| 在所有测试方法前执行一次,一般在其中写上整体初始化的代码 |
| 在所有测试方法后执行一次,一般在其中写上销毁和释放资源的代码 |
| 在每个测试方法前执行,一般用来初始化方法(比如我们在测试别的方法时,类中与其他测试方法共享的值已经被改变,为了保证测试结果的有效性,我们会在@Before注解的方法中重置数据) |
| 在每个测试方法后执行,在方法执行完成后要做的事情 |
| 编写一般测试用例 |
| 测试方法执行超过1000毫秒后算超时,测试将失败 |
| 测试方法期望得到的异常类,如果方法执行没有抛出指定的异常,则测试失败 |
| 执行测试时将忽略掉此方法,如果用于修饰类,则忽略整个类 |
Assert
方法 | 功能 |
| 判断A与B是否相等,调用equals方法,如不相等则报错 |
| 判断A与B是否相等,使用==操作符,如不相等则报错 |
| 判断A条件是否为真,如不为真则报错 |
| 判断A条件是否不为真,如不符合则报错 |
| 判断A是否不为空,如为空则报错 |
| 判断A数组与B数组是否相等,如不相等则报错 |
JUnit4支持assertThat()等更方便灵活的断言方法
Dao层的单元测试
Dao层的测试需要准备一个空数据库,以及一些初始化的数据,使用@Sql
注解来初始化。
首先在classpath下准备一个初始化数据的脚本文件user.sql
,内容如下:
INSERT INTO `user` (`user_id`, `name`) VALUES (99, '大叔');
测试代码:
// @ActiveProfiles激活application-test.properties文件和@profile注解作为profile,使用准备好的空数据库环境
@ActiveProfiles("test")
// @Transactional测试执行后回滚数据
@Transactional
// 执行sql,‘/’开头表示从classpath根目录开始搜索,没有以此开头默认在测试类所在包下。也可使用classpath:、file:、http: 开头
@Sql({"/user.sql"})
// @Runwith是JUnit标准的一个注解,Spring的单元测试都用SpringRunner.class
@RunWith(SpringRunner.class)
// @SpringBootTest用于Spring Boot应用测试,默认根据报名逐级往上寻找应用启动类
@SpringBootTest
public class UserDaoTest {
@Autowired
private UserDao userDao;
@Test
public void testGetOne() {
User user = userDao.getOne(99);
Assert.assertNotNull(user);
Assert.assertEquals("大叔", user.getName());
Assert.assertSame("不一样!", 99, user.getId());
}
}
Service层的单元测试
Service层是处理业务逻辑的地方,通常比较复杂,编写单元测试代码前需要处理好以下三个问题:
- 保证可重复测试。一个service方法可以多次测试,因此测试完毕后数据要能自动回滚。
- 模拟未完成的Service。当前测试的Service依赖的其它Service未开发完毕时,要能模拟其它Service。
- 准备测试数据。单元测试前要模拟好测试场景的数据。
如何解决呢?
- 对于第一个问题,可以使用Spring提供的
@Transactional
注解进行事务回滚。 - 对于第二个问题,使用Spring Boot集成的
Mockito
来模拟未完成的Service或者不能随便调用的Service。 - 对于第三个问题,
@Sql
注解。
下面来看一下使用 Mockito 解决第二个问题的示例。
假设现有UserService
依赖于CreditSystemService
的getUserCredit(int id)
方法,但是CreditSystemService
并没有实现。
@Transactional
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceImplTest {
// UserService中依赖了CreditSystemService,但是CreditSystemService没有实现类
@Autowired
private UserService userService;
// 注解@MockBean 可以自动注入Spring管理的Service,这里creditSystemService由Mockito工具产生。
@MockBean
private CreditSystemService creditSystemService;
@Test
public void getCredit() throws Exception {
// given(需要模拟的方法).willReturn(模拟返回的结果)
BDDMockito.given(creditSystemService.getUserCredit(BDDMockito.anyInt())).willReturn(100);
Assert.assertEquals(100, userService.getCredit(userId));
}
}
Controller层的单元测试
Spring MVC Test 通过@WebMvcTest
来完成MVC单元测试。示例代码如下:
@RunWith(SpringRunner.class)
// @WebMvcTest表示这是一个MVC测试,只限于单个Controller。
@WebMvcTest(UserController.class)
public class UserControllerTest {
// MockMvc是Spring专门提供用于测试SpringMVC类
@Autowired
private MockMvc mockMvc;
// @MockBean 用来模拟实现,因为在Spring MVC测试中并不会初始化@Service注解的类,需要自己模拟service实现。
@MockBean
private UserService userService;
//MockMvc模拟的session
//@Autowired
//private MockHttpSession session;
//@Before
//public void setupMockMvc(){
//User user =new User(99,"大叔");
//session.setAttribute("user",user); //给session里放个attribute
//}
@Test
public void getUser() throws Exception {
int userId = 99;
BDDMockito.given(userService.getCredit(userId)).willReturn(100);
// perform,完成一次MVC调用,Spring MVC Test是servlet容器内部的模拟测试,不会发起真正的HTTP请求。
// get,模拟一次GET请求。
// andExpect,表示请求期望的返回结果
mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", userId))
.andExpect(status().isOk()).andExpect(MockMvcResultMatchers.content().string(String.valueOf(100)));
}
}
MockMvc方法 | 功能 |
| 执行一个MockHttpServletRequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处。 |
| 添加request的参数,如发送请求的时候带上 |
| 模拟注入一个MockHttpSession。 |
| 模拟注入一个Cookie。 |
| 添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确(对返回的数据进行的判断),StatusResultMatchers对请求结果进行验证,ContentResultMatchers对请求返回的内容进行验证。 |
| 添加ResultHandler结果处理器,比如调试时打印结果到控制台(对返回的数据进行的判断)。 |
| 最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理(对返回的数据进行的判断)。 |