詳解Spring Boot實(shí)戰(zhàn)之單元測試
本文介紹使用Spring測試框架提供的MockMvc對(duì)象,對(duì)Restful API進(jìn)行單元測試
Spring測試框架提供MockMvc對(duì)象,可以在不需要客戶端-服務(wù)端請(qǐng)求的情況下進(jìn)行MVC測試,完全在服務(wù)端這邊就可以執(zhí)行Controller的請(qǐng)求,跟啟動(dòng)了測試服務(wù)器一樣。
測試開始之前需要建立測試環(huán)境,setup方法被@Before修飾。通過MockMvcBuilders工具,使用WebApplicationContext對(duì)象作為參數(shù),創(chuàng)建一個(gè)MockMvc對(duì)象。
MockMvc對(duì)象提供一組工具函數(shù)用來執(zhí)行assert判斷,都是針對(duì)web請(qǐng)求的判斷。這組工具的使用方式是函數(shù)的鏈?zhǔn)秸{(diào)用,允許程序員將多個(gè)測試用例鏈接在一起,并進(jìn)行多個(gè)判斷。在這個(gè)例子中我們用到下面的一些工具函數(shù):
perform(get(...))建立web請(qǐng)求。在我們的第三個(gè)用例中,通過MockMvcRequestBuilder執(zhí)行GET請(qǐng)求。
andExpect(...)可以在perform(...)函數(shù)調(diào)用后多次調(diào)用,表示對(duì)多個(gè)條件的判斷,這個(gè)函數(shù)的參數(shù)類型是ResultMatcher接口,在MockMvcResultMatchers這這個(gè)類中提供了很多返回ResultMatcher接口的工具函數(shù)。這個(gè)函數(shù)使得可以檢測同一個(gè)web請(qǐng)求的多個(gè)方面,包括HTTP響應(yīng)狀態(tài)碼(response status),響應(yīng)的內(nèi)容類型(content type),會(huì)話中存放的值,檢驗(yàn)重定向、model或者h(yuǎn)eader的內(nèi)容等等。這里需要通過第三方庫json-path檢測JSON格式的響應(yīng)數(shù)據(jù):檢查json數(shù)據(jù)包含正確的元素類型和對(duì)應(yīng)的值,例如jsonPath("$.name").value("中文測試")用于檢查在根目錄下有一個(gè)名為name的節(jié)點(diǎn),并且該節(jié)點(diǎn)對(duì)應(yīng)的值是“testuser”。
本文對(duì)rest api的開發(fā)不做詳細(xì)描述,如需了解可以參考 Spring Boot實(shí)戰(zhàn)之Rest接口開發(fā)及數(shù)據(jù)庫基本操作
1、修改pom.xml,添加依賴庫json-path,用于檢測JSON格式的響應(yīng)數(shù)據(jù)
<dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> </dependency>
2、添加用戶數(shù)據(jù)模型UserInfo.java
package com.xiaofangtech.sunt.bean;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Size;
@Entity
@Table(name="t_userinfo")
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Size(min=0, max=32)
private String name;
private Integer age;
@Size(min=0, max=255)
private String address;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
3、添加控制器UserController.java,用于實(shí)現(xiàn)對(duì)用戶的增刪改查
package com.xiaofangtech.sunt.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.xiaofangtech.sunt.bean.UserInfo;
import com.xiaofangtech.sunt.repository.UserInfoRepository;
import com.xiaofangtech.sunt.utils.*;
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserInfoRepository userRepositoy;
/***
* 根據(jù)用戶id,獲取用戶信息
* @param id
* @return
*/
@RequestMapping(value="getuser", method=RequestMethod.GET)
public Object getUser(Long id)
{
UserInfo userEntity = userRepositoy.findOne(id);
ResultMsg resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(), ResultStatusCode.OK.getErrmsg(), userEntity);
return resultMsg;
}
/***
* 獲取所有用戶列表
* @return
*/
@RequestMapping(value="getalluser", method=RequestMethod.GET)
public Object getUserList()
{
List<UserInfo> userEntities = (List<UserInfo>) userRepositoy.findAll();
ResultMsg resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(), ResultStatusCode.OK.getErrmsg(), userEntities);
return resultMsg;
}
/***
* 新增用戶信息
* @param userEntity
* @return
*/
@Modifying
@RequestMapping(value="adduser", method=RequestMethod.POST)
public Object addUser(@RequestBody UserInfo userEntity)
{
userRepositoy.save(userEntity);
ResultMsg resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(), ResultStatusCode.OK.getErrmsg(), userEntity);
return resultMsg;
}
/***
* 更新用戶信息
* @param userEntity
* @return
*/
@Modifying
@RequestMapping(value="updateuser", method=RequestMethod.PUT)
public Object updateUser(@RequestBody UserInfo userEntity)
{
UserInfo user = userRepositoy.findOne(userEntity.getId());
if (user != null)
{
user.setName(userEntity.getName());
user.setAge(userEntity.getAge());
user.setAddress(userEntity.getAddress());
userRepositoy.save(user);
}
ResultMsg resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(), ResultStatusCode.OK.getErrmsg(), user);
return resultMsg;
}
/***
* 刪除用戶
* @param id
* @return
*/
@Modifying
@RequestMapping(value="deleteuser", method=RequestMethod.DELETE)
public Object deleteUser(Long id)
{
try
{
userRepositoy.delete(id);
}
catch(Exception exception)
{
}
ResultMsg resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(), ResultStatusCode.OK.getErrmsg(), null);
return resultMsg;
}
}
4、修改測試類,添加對(duì)以上接口進(jìn)行單元測試的測試用例
package com.xiaofangtech.sunt;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaofangtech.sunt.bean.UserInfo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.Matchers.*;
//這是JUnit的注解,通過這個(gè)注解讓SpringJUnit4ClassRunner這個(gè)類提供Spring測試上下文。
@RunWith(SpringJUnit4ClassRunner.class)
//這是Spring Boot注解,為了進(jìn)行集成測試,需要通過這個(gè)注解加載和配置Spring應(yīng)用上下
@SpringApplicationConfiguration(classes = SpringJUnitTestApplication.class)
@WebAppConfiguration
public class SpringJUnitTestApplicationTests {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setupMockMvc() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
/***
* 測試添加用戶接口
* @throws Exception
*/
@Test
public void testAddUser() throws Exception
{
//構(gòu)造添加的用戶信息
UserInfo userInfo = new UserInfo();
userInfo.setName("testuser2");
userInfo.setAge(29);
userInfo.setAddress("北京");
ObjectMapper mapper = new ObjectMapper();
//調(diào)用接口,傳入添加的用戶參數(shù)
mockMvc.perform(post("/user/adduser")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(mapper.writeValueAsString(userInfo)))
//判斷返回值,是否達(dá)到預(yù)期,測試示例中的返回值的結(jié)構(gòu)如下{"errcode":0,"errmsg":"OK","p2pdata":null}
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
//使用jsonPath解析返回值,判斷具體的內(nèi)容
.andExpect(jsonPath("$.errcode", is(0)))
.andExpect(jsonPath("$.p2pdata", notNullValue()))
.andExpect(jsonPath("$.p2pdata.id", not(0)))
.andExpect(jsonPath("$.p2pdata.name", is("testuser2")));
}
/***
* 測試更新用戶信息接口
* @throws Exception
*/
@Test
public void testUpdateUser() throws Exception
{
//構(gòu)造添加的用戶信息,更新id為2的用戶的用戶信息
UserInfo userInfo = new UserInfo();
userInfo.setId((long)2);
userInfo.setName("testuser");
userInfo.setAge(26);
userInfo.setAddress("南京");
ObjectMapper mapper = new ObjectMapper();
mockMvc.perform(put("/user/updateuser")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(mapper.writeValueAsString(userInfo)))
//判斷返回值,是否達(dá)到預(yù)期,測試示例中的返回值的結(jié)構(gòu)如下
//{"errcode":0,"errmsg":"OK","p2pdata":null}
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.errcode", is(0)))
.andExpect(jsonPath("$.p2pdata", notNullValue()))
.andExpect(jsonPath("$.p2pdata.id", is(2)))
.andExpect(jsonPath("$.p2pdata.name", is("testuser")))
.andExpect(jsonPath("$.p2pdata.age", is(26)))
.andExpect(jsonPath("$.p2pdata.address", is("南京")));
}
/***
* 測試根據(jù)用戶id獲取用戶信息接口
* @throws Exception
*/
@Test
public void testGetUser() throws Exception
{
mockMvc.perform(get("/user/getuser?id=2"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.errcode", is(0)))
.andExpect(jsonPath("$.p2pdata", notNullValue()))
.andExpect(jsonPath("$.p2pdata.id", is(2)))
.andExpect(jsonPath("$.p2pdata.name", is("testuser")))
.andExpect(jsonPath("$.p2pdata.age", is(26)))
.andExpect(jsonPath("$.p2pdata.address", is("南京")));
}
/***
* 測試獲取用戶列表接口
* @throws Exception
*/
@Test
public void testGetUsers() throws Exception
{
mockMvc.perform(get("/user/getalluser"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.errcode", is(0)))
.andExpect(jsonPath("$.p2pdata", notNullValue()));
}
}
5、運(yùn)行測試,執(zhí)行JUnit Test

一共執(zhí)行4個(gè)測試用例,全都通過

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java實(shí)現(xiàn)String類型和Date類型相互轉(zhuǎn)換
很多人表示,java將string類型轉(zhuǎn)為date類型不知道應(yīng)該怎樣做,本文就來介紹一下java實(shí)現(xiàn)String類型和Date類型相互轉(zhuǎn)換,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
SpringBoot中Tomcat和SpringMVC整合源碼分析
Tomcat和SpringMVC都是通過這樣的方式進(jìn)行集成的,SpringBoot出現(xiàn)之前SpringMVC項(xiàng)目是直接部署在Tomcat服務(wù)器中的,這篇文章主要介紹了SpringBoot中Tomcat和SpringMVC整合源碼分析,需要的朋友可以參考下2022-07-07
spring boot 使用@Async實(shí)現(xiàn)異步調(diào)用方法
本篇文章主要介紹了spring boot 使用@Async實(shí)現(xiàn)異步調(diào)用方法,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-04-04
springboot項(xiàng)目mysql-connector-java默認(rèn)版本如何查看
這篇文章主要介紹了springboot項(xiàng)目mysql-connector-java默認(rèn)版本如何查看問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
java搭建一個(gè)Socket服務(wù)器響應(yīng)多用戶訪問
本篇文章主要介紹了java搭建一個(gè)Socket服務(wù)器響應(yīng)多用戶訪問,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02
Spring Cloud Gateway + Nacos 實(shí)現(xiàn)動(dòng)態(tài)路由
這篇文章主要介紹了Spring Cloud Gateway + Nacos 實(shí)現(xiàn)動(dòng)態(tài)路由的方法,幫助大家實(shí)現(xiàn)路由信息的自動(dòng)更新,感興趣的朋友可以了解下2020-10-10
Java使用注解實(shí)現(xiàn)BigDecimal的四舍五入
BigDecimal是Java中的一個(gè)類,位于java.math包中,它提供了任意精度的有符號(hào)十進(jìn)制數(shù)字的表示,以及對(duì)這些數(shù)字進(jìn)行算術(shù)運(yùn)算的方法,本文介紹了Java使用注解實(shí)現(xiàn)BigDecimal的四舍五入的相關(guān)知識(shí),需要的朋友可以參考下2024-09-09
SpringBoot中MyBatis-Flex的集成和使用實(shí)現(xiàn)
MyBatis-Flex是一個(gè)基于MyBatis的數(shù)據(jù)訪問框架,MyBatis-Flex能夠極大地提高我們的開發(fā)效率和開發(fā)體驗(yàn),本文主要介紹了SpringBoot中MyBatis-Flex的集成和使用實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12

