Spring Boot项目Test启动器进行单元和API测试详解
引入test启动器
maven配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
依赖关系还引入那些的包
依赖关系图说明:
spring-boot-test:测试的核心内容。
spring-boot-test-autoconfigure:测试的自动化配置。
Json-path:JSON操作类库。
junit:Java 应用程序单元测试标准类库。
assertj-core:轻量级的断言类库。
mockito-core:Java Mock测试框架。
hamcrest-core:对象匹配器类库。
hamcrest-library:对象匹配器类库。
jsonassert:JSON的断言库。
spring-core:spring集成测试类库。
spring-test:spring集成测试类库。
xmlunit-core:XML单元测试类库。
项目实战
单元测试类的方法
配置JUnitGenerator V2.0(为了偷懒,可以跳过)
通用配置模板配置
JUnit4.x常用注解说明
@BeforeClass 在所有测试方法前执行一次,一般在方法里面写整体初始化的代码
@AfterClass 在所有测试方法后执行一次,一般方在法里面写销毁和释放资源的代码
@Before 在每个测试方法前执行,一般用来初始化方法
@After 在每个测试方法后执行,在方法执行完成后要做的事情
@Test(timeout = 1000) 测试方法执行超时,超过1000毫秒后算超时,测试将失败
@Test(expected = Exception.class) 测试方法期望得到的异常类,如果方法执行没有抛出指定的异常,则测试失败
@Ignore(“not ready yet”) 执行测试时将忽略掉此方法,如果用于修饰类,则忽略整个类
@Test 编写一般测试用例
@RunWith 在JUnit中有很多个Runner,他们负责调用你的测试代码,每一个Runner都有各自的特殊功能,你要根据需要选择不同的Runner来运行你的测试代码。
一个测试类单元测试的执行顺序为:
@BeforeClass –> @Before –> @Test –> @After –> @AfterClass
一个测试方法的调用顺序为:
@Before –> @Test –> @After
断言函数总结
void assertEquals(boolean expected, boolean actual) //检查两个变量或者等式是否平衡
void assertTrue(boolean expected, boolean actual) //检查条件为真
void assertFalse(boolean condition)//检查条件为假
void assertNotNull(Object object)//检查对象不为空
void assertNull(Object object)//检查对象为空
void assertSame(boolean condition)//assertSame() 方法检查两个相关对象是否指向同一个对象
void assertNotSame(boolean condition)//方法检查两个相关对象是否不指向同一个对象
void assertArrayEquals(expectedArray, resultArray)//方法检查两个数组是否相等
实战
在需要编写单元测试的类文件中按快捷键,调用模板的方法(Alt+Insert)默认测试所有所有方法。
通过模板创建测试类
编写单元测试代码,这里是测试github项目用户模块service层的接口实现
package com.springboot.action.saas.modules.user.service.impl;
import com.springboot.action.saas.modules.user.domain.UserMember;
import com.springboot.action.saas.modules.user.dto.UserDto;
import com.springboot.action.saas.modules.user.repository.UserMemberRepository;
import com.springboot.action.saas.modules.user.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/*
* 业务接口是实现
* */
@Service
public class MemberServiceImpl implements MemberService {
@Autowired
private UserMemberRepository userMemberRepository;
@Override
public Long addMember(UserDto member) {
//检查用户名是否已经存在
if(null != userMemberRepository.findByName(member.getName())){
//走存在处理分支
return -1L;
}
UserMember userMember = new UserMember();
//用户姓名
userMember.setName(member.getName());
//加密密码
BCryptPasswordEncoder encode = new BCryptPasswordEncoder();
userMember.setPassWord(encode.encode(member.getPassword()));
//设置创建时间
userMember.setCreateTime(System.currentTimeMillis());
//加入到数据库
userMemberRepository.saveAndFlush(userMember);
return userMember.getId();
}
@Override
public UserDto findMemberById(Long id) {
UserDto userDto = new UserDto();
Optional<UserMember> userMemberOptional = userMemberRepository.findById(id);
if(!userMemberOptional.isPresent()){
//用户不存在,走不存在处理
return null;
}
//domain to dto
UserMember userMember = userMemberOptional.get();
userDto.setId(userMember.getId());
userDto.setName(userMember.getName());
userDto.setPassword(userMember.getPassWord());
userDto.setEnabled(userMember.getEnabled());
userDto.setCreateTime(userMember.getCreateTime());
userDto.setPasswordResetDate(userMember.getPasswordResetDate());
return userDto;
}
@Override
public UserDto findMemberByName(String name) {
UserMember userMember = userMemberRepository.findByName(name);
if (null == userMember) {
//用户不存在,走不存在处理
return null;
}
//domain to dto
UserDto userDto = new UserDto();
userDto.setId(userMember.getId());
userDto.setName(userMember.getName());
userDto.setPassword(userMember.getPassWord());
userDto.setEnabled(userMember.getEnabled());
userDto.setCreateTime(userMember.getCreateTime());
userDto.setPasswordResetDate(userMember.getPasswordResetDate());
return userDto;
}
@Override
public List<UserDto> findAllMember() {
List<UserMember> userMemberList = userMemberRepository.findAll();
List<UserDto> userDtoList = new ArrayList<>();;
for (int i = 0; i < userMemberList.size(); i++) {
UserMember userMember = (UserMember) userMemberList.get(i);
//domain to dto
UserDto userDto = new UserDto();
userDto.setId(userMember.getId());
userDto.setName(userMember.getName());
userDto.setEnabled(userMember.getEnabled());
userDto.setCreateTime(userMember.getCreateTime());
userDtoList.add(userDto);
}
return userDtoList;
}
@Override
public void updateMember(UserDto member) {
UserMember userMember = new UserMember();
//加密密码
userMember.setId(member.getId());
userMember.setName(member.getName());
userMember.setPassWord(member.getPassword());
userMember.setEnabled(member.getEnabled());
userMember.setCreateTime(member.getCreateTime());
userMemberRepository.saveAndFlush(userMember);
}
}
测试http请求业务接口
通过使用Spring Test中的MockMvc包的builder类向 DispatcherServlet 发送HTTP请求,并对结果作出断言。本次演示测试github项目用户controller请求接口
package com.springboot.action.saas.modules.user.controller;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* MemberController Tester.
*
* @author <Authors name>
* @version 1.0
* @since <pre>11/09/2019</pre>
*/
//运行器注解
@RunWith(SpringRunner.class)
//spring boot 测试注解,测试中使用完全运行的web服务器
@SpringBootTest
//由于是Web项目,Junit需要模拟ServletContext,需要给测试类加上@WebAppConfiguration。
@WebAppConfiguration
//注入一个MockMvc实例
@AutoConfigureMockMvc
public class MemberControllerTest {
@Autowired
private MockMvc mockMvc;
@Before
public void before() throws Exception {
System.out.println("MemberControllerTest before");
}
@After
public void after() throws Exception {
System.out.println("MemberControllerTest after");
}
/**
* Method: findById(@PathVariable("id") Long id)
*/
@Test
public void testFindById() throws Exception {
//TODO: Test goes here...
//请求的url,请求的方法是get,添加参数
RequestBuilder requestBuilder = get("/member/findById")
.param("id", "1");
String responseString = mockMvc.perform(requestBuilder)
.andExpect(status().isOk())
//返回的状态是200
.andExpect(MockMvcResultMatchers.jsonPath("$.state").value(1))
//判断某返回值是否符合预期
.andDo(print())
//打印出请求和相应的内容
.andReturn()
.getResponse()
.getContentAsString();
//将相应的数据转换为字符串
System.out.println("get方法/member/findById,{}"+responseString);
}
}