参考视频:用Spring Boot编写RESTful API
参考链接:Spring Boot构建RESTful API与单元测试
参考链接:Junit自动单元测试以及测试覆盖率简单使用
一、junit断言
函数 | 作用 |
---|---|
TestCase.assertTrue | 判断条件是否为真 |
TestCase.assertFalse | 判断条件是否为假 |
TestCase.assertEquals(val1,val2) | 判断val1是否和val2相等 |
TestCase.assertNotSame(val1,val2) | 判断val1是否和val2不相等 |
Assert.assertArrayEquals(array1,array2) | 判断两个数组相等 |
TestCase.fail(message) | 测试直接失败,抛出message信息 |
注意:
assertTrue(val1 == val2) 是判断val1和val2是否是同一个实例
assertEquals(val1, val2) 是判断val1和val2值是否相等
Assert中有许多方法被淘汰了,这里建议多用TestCase
二、测试模块
- 测试模块:我们需要写测试代码的模块
- 驱动模块:调用我们测试代码的模块
- 桩模块 :模拟被测试的模块所调用的模块,而不是软件产品的组成的部分,这个桩模块本身不执行任何功能仅在被调用时返回静态值来模拟被调用模块的行为
桩模块简单实例(税收计算较复杂,可以先模仿一下,作为桩模块,这样就可以不影响被测模块的测试):
三、使用Mockito作为桩模块
我们在maven中加入Mockito和junit的依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
数据库访问层studentDao如下(还未实现方法):
import org.springframework.stereotype.Repository;
import whu.xsy.swagger_use.entity.student;
import java.util.List;
@Repository
public interface studentDao {
//TODO 从数据库获取所有学生信息
List<student> getAll();
}
service层如下(调用studentDao):
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import whu.xsy.swagger_use.dao.studentDao;
import whu.xsy.swagger_use.entity.student;
import java.util.List;
@Service
public class studentService {
@Autowired
studentDao studentDao;
public List<student> getAll(){
return studentDao.getAll();
}
}
接下来开始设置桩模块并开始测试
此时数据库并没有数据,数据访问层dao也没写好,那么该如何测试service呢?
我们使用mockito在dao层调用特定方法时模拟返回数据,这样就可以不影响service层的测试,并且即使数据库发生变化(比如环境迁移),也不影响测试。
简而言之,就是mockito接管了dao层
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import whu.xsy.swagger_use.dao.studentDao;
import whu.xsy.swagger_use.entity.student;
import whu.xsy.swagger_use.service.studentService;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class SwaggerUseApplicationTests {
@MockBean
studentDao studentDao;
@Autowired
studentService studentService;
@Test
public void contextLoads(){} // 可用来检测springboot环境
//测试studentService
@Test
public void testStudentService(){
//模拟数据库中数据
List<student> students= new ArrayList<>();
students.add(new student(1,"xsy"));
students.add(new student(2,"theory"));
//由于 studentDao 的 getAll() 方法还没写好,所以使用 Mockito 做桩模块,模拟返回数据
Mockito.when(studentDao.getAll()).thenReturn( students );
//调用studentService.getAll()
List<student> result = studentService.getAll();
//断言
TestCase.assertEquals(2,result.size());
TestCase.assertEquals("xsy",result.get(0).getName());
}
}
四、使用mockMvc测试web层
- 在test类上加上
@AutoConfigureMockMvc
注解 - 自动注入
MockMvc mockMvc;
- 对studentController中增删改查接口进行测试
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import whu.xsy.swagger_use.entity.student;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/student")
@Api(value = "/student", tags = "学生模块") //标注在类上的
public class studentController {
//模拟数据库
private static List<student> students = new ArrayList<>();
//初始化模拟数据库
static{
students.add(new student(1,"xsy"));
students.add(new student(2,"theory"));
}
@GetMapping("")
public List<student> getAll(){
return students;
}
@PostMapping("")
public boolean add(student student){
return students.add(student);
}
}
测试get方法(期望状态200,返回的json中含有xsy,打印返回的信息)
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
@AutoConfigureMockMvc
public class SwaggerUseApplicationTests {
@MockBean
studentDao studentDao;
@Autowired
studentService studentService;
@Autowired
MockMvc mockMvc;
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(new studentController()).build();
}
@Test
public void testStudentController() throws Exception {
RequestBuilder request = null;
//查询所有学生
request = get("/student/");
mockMvc.perform(request)
.andExpect(status().isOk())
.andDo(MockMvcResultHandlers.print())
.andExpect(content().string(Matchers.containsString("xsy")));
}
}
测试通过,并且打印的信息如下:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /student/
Parameters = {}
Headers = []
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = whu.xsy.swagger_use.controller.studentController
Method = whu.xsy.swagger_use.controller.studentController#getAll()
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = [{"id":1,"name":"xsy"},{"id":2,"name":"theory"}]
Forwarded URL = null
Redirected URL = null
Cookies = []
测试post方法:
本实例中展现了MockMvc如何上传数据和获取返回结果
测试方法与get类似
request = post("/student/")
.param("id","3")
.param("name","ys");
String result = mockMvc.perform(request)
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
TestCase.assertEquals("true",result);
五、批量测试和测试覆盖率
我们将service的测试和controller的测试分开:
我们需要一次性运行所有的单元测试,则需要配置一次性运行whu.xsy.swagger_use中的所有test类(具体配置见Junit自动单元测试以及测试覆盖率简单使用)
然后点击run with coverage
就可以运行所有的测试文件,并且可以看到覆盖率