如何用SpringBoot 進(jìn)行測(cè)試
普通測(cè)試
假設(shè)要測(cè)試一個(gè)工具類(lèi) StringUtil(com.rxliuli.example.springboottest.util.StringUtil)
/**
* 用于測(cè)試的字符串工具類(lèi)
*
* @author rxliuli
*/
public class StringUtil {
/**
* 判斷是否為空
*
* @param string 要進(jìn)行判斷的字符串
* @return 是否為 null 或者空字符串
*/
public static boolean isEmpty(String string) {
return string == null || string.isEmpty();
}
/**
* 判斷是否為空
*
* @param string 要進(jìn)行判斷的字符串
* @return 是否為 null 或者空字符串
*/
public static boolean isNotEmpty(String string) {
return !isEmpty(string);
}
/**
* 判斷是否有字符串為空
*
* @param strings 要進(jìn)行判斷的一個(gè)或多個(gè)字符串
* @return 是否有 null 或者空字符串
*/
public static boolean isAnyEmpty(String... strings) {
return Arrays.stream(strings)
.anyMatch(StringUtil::isEmpty);
}
/**
* 判斷字符串是否全部為空
*
* @param strings 要進(jìn)行判斷的一個(gè)或多個(gè)字符串
* @return 是否全部為 null 或者空字符串
*/
public static boolean isAllEmpty(String... strings) {
return Arrays.stream(strings)
.allMatch(StringUtil::isEmpty);
}
}
需要添加依賴(lài) spring-boot-starter-test 以及指定 assertj-core 的最新版本
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.9.1</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement>
這里指定 assertj-core 的版本是為了使用較新的一部分?jǐn)嘌怨δ埽ɡ鐚傩?lambda 斷言)
/**
* @author rxliuli
*/
public class StringUtilTest {
private String strNull = null;
private String strEmpty = "";
private String strSome = "str";
@Test
public void isEmpty() {
//測(cè)試 null
assertThat(StringUtil.isEmpty(strNull))
.isTrue();
//測(cè)試 empty
assertThat(StringUtil.isEmpty(strEmpty))
.isTrue();
//測(cè)試 some
assertThat(StringUtil.isEmpty(strSome))
.isFalse();
}
@Test
public void isNotEmpty() {
//測(cè)試 null
assertThat(StringUtil.isNotEmpty(strNull))
.isFalse();
//測(cè)試 empty
assertThat(StringUtil.isNotEmpty(strEmpty))
.isFalse();
//測(cè)試 some
assertThat(StringUtil.isNotEmpty(strSome))
.isTrue();
}
@Test
public void isAnyEmpty() {
assertThat(StringUtil.isAnyEmpty(strNull, strEmpty, strSome))
.isTrue();
assertThat(StringUtil.isAnyEmpty())
.isFalse();
}
@Test
public void isAllEmpty() {
assertThat(StringUtil.isAllEmpty(strNull, strEmpty, strSome))
.isFalse();
assertThat(StringUtil.isAnyEmpty(strNull, strEmpty))
.isTrue();
}
}
這里和非 SpringBoot 測(cè)試時(shí)沒(méi)什么太大的區(qū)別,唯一的一點(diǎn)就是引入 Jar 不同,這里雖然我們只引入了 spring-boot-starter-test,但它本身已經(jīng)幫我們引入了許多的測(cè)試相關(guān)類(lèi)庫(kù)了。
Dao/Service 測(cè)試
從這里開(kāi)始就和標(biāo)準(zhǔn)的 Spring 不太一樣了
首先,我們需要 Dao 層,這里使用 H2DB 和 SpringJDBC 做數(shù)據(jù)訪(fǎng)問(wèn)層(比較簡(jiǎn)單)。
依賴(lài)
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
添加兩個(gè)初始化腳本
數(shù)據(jù)庫(kù)結(jié)構(gòu) db_schema.sql(db/db_schema.sql)
drop table if exists user; create table user ( id int auto_increment not null comment '編號(hào)', name varchar(20) not null comment '名字', sex boolean null comment '性別', age int null comment '年齡' );
數(shù)據(jù)庫(kù)數(shù)據(jù) db_data.sql(db/db_data.sql)
insert into user (id, name, sex, age) values (1, '琉璃', false, 17), (2, '月姬', false, 1000);
為 SpringBoot 配置一下數(shù)據(jù)源及初始化腳本
spring: datasource: driver-class-name: org.h2.Driver platform: h2 schema: classpath:db/db_schema.sql data: classpath:db/db_data.sql
然后是實(shí)體類(lèi)與 Dao
用戶(hù)實(shí)體類(lèi) User(com.rxliuli.example.springboottest.entity.User)
/**
* @author rxliuli
*/
public class User implements Serializable {
private Integer id;
private String name;
private Boolean sex;
private Integer age;
public User() {
}
public User(String name, Boolean sex, Integer age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public User(Integer id, String name, Boolean sex, Integer age) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
}
//getter() and setter()
}
用戶(hù) Dao UserDao(com.rxliuli.example.springboottest.dao.UserDao)
/**
* @author rxliuli
*/
@Repository
public class UserDao {
private final RowMapper<User> userRowMapper = (rs, rowNum) -> new User(
rs.getInt("id"),
rs.getString("name"),
rs.getBoolean("sex"),
rs.getInt("age")
);
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 根據(jù) id 獲取一個(gè)對(duì)象
*
* @param id id
* @return 根據(jù) id 查詢(xún)到的對(duì)象,如果沒(méi)有查到則為 null
*/
public User get(Integer id) {
return jdbcTemplate.queryForObject("select * from user where id = ?", userRowMapper, id);
}
/**
* 查詢(xún)?nèi)坑脩?hù)
*
* @return 全部用戶(hù)列表
*/
public List<User> listForAll() {
return jdbcTemplate.query("select * from user", userRowMapper);
}
/**
* 根據(jù) id 刪除用戶(hù)
*
* @param id 用戶(hù) id
* @return 受影響行數(shù)
*/
public int deleteById(Integer id) {
return jdbcTemplate.update("delete from user where id = ?", id);
}
}
接下來(lái)才是正事,測(cè)試 Dao 層需要加載 Spring 容器,自動(dòng)回滾以避免污染數(shù)據(jù)庫(kù)。
/**
* {@code @SpringBootTest} 和 {@code @RunWith(SpringRunner.class)} 是必須的,這里貌似一直有人誤會(huì)需要使用 {@code @RunWith(SpringJUnit4ClassRunner.class)},但其實(shí)并不需要了
* 下面的 {@code @Transactional} 和 {@code @Rollback}則是開(kāi)啟事務(wù)控制以及自動(dòng)回滾
*
* @author rxliuli
*/
@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
@Rollback
public class UserDaoTest {
@Autowired
private UserDao userDao;
@Test
public void get() {
int id = 1;
User result = userDao.get(id);
//斷言 id 和 get id 相同
assertThat(result)
.extracting(User::getId)
.contains(id);
}
@Test
public void listForAll() {
List<User> userList = userDao.listForAll();
//斷言不為空
assertThat(userList)
.isNotEmpty();
}
@Test
public void deleteById() {
int result = userDao.deleteById(1);
assertThat(result)
.isGreaterThan(0);
}
}
Web 測(cè)試
與傳統(tǒng)的 SpringTest 一樣,SpringBoot 也分為兩種。
- 獨(dú)立安裝測(cè)試:
手動(dòng)加載單個(gè) Controller,所以測(cè)試其他 Controller 中的接口會(huì)發(fā)生異常。但測(cè)試速度上較快,所以應(yīng)當(dāng)優(yōu)先選擇。
- 集成 Web 環(huán)境測(cè)試:
將啟動(dòng)并且加載所有的 Controller, 所以效率上之于 BaseWebUnitTest 來(lái)說(shuō)非常低下, 僅適用于集成測(cè)試多個(gè) Controller 時(shí)使用。
獨(dú)立安裝測(cè)試
主要是設(shè)置需要使用的 Controller 實(shí)例,然后用獲得 MockMvc 對(duì)象進(jìn)行測(cè)試即可。
/**
* @author rxliuli
*/
@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
@Rollback
public class UserControllerUnitTest {
@Autowired
private UserController userController;
/**
* 用于測(cè)試 API 的模擬請(qǐng)求對(duì)象
*/
private MockMvc mockMvc;
@Before
public void before() {
//模擬一個(gè) Mvc 測(cè)試環(huán)境,獲取一個(gè) MockMvc 實(shí)例
mockMvc = MockMvcBuilders.standaloneSetup(userController)
.build();
}
@Test
public void testGet() throws Exception {
//測(cè)試能夠正常獲取
Integer id = 1;
mockMvc.perform(
//發(fā)起 get 請(qǐng)求
get("/user/" + id)
)
//斷言請(qǐng)求的狀態(tài)是成功的(200)
.andExpect(status().isOk())
//斷言返回對(duì)象的 id 和請(qǐng)求的 id 相同
.andExpect(jsonPath("$.id").value(id));
}
@Test
public void listForAll() throws Exception {
//測(cè)試正常獲取
mockMvc.perform(
//發(fā)起 post 請(qǐng)求
post("/user/listForAll")
)
//斷言請(qǐng)求狀態(tài)
.andExpect(status().isOk())
//斷言返回結(jié)果是數(shù)組
.andExpect(jsonPath("$").isArray())
//斷言返回?cái)?shù)組不是空的
.andExpect(jsonPath("$").isNotEmpty());
}
}
集成 Web 環(huán)境測(cè)試
/**
* @author rxliuli
*/
@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
@Rollback
public class UserControllerIntegratedTest {
@Autowired
private WebApplicationContext context;
/**
* 用于測(cè)試 API 的模擬請(qǐng)求對(duì)象
*/
private MockMvc mockMvc;
@Before
public void before() {
//這里把整個(gè) WebApplicationContext 上下文都丟進(jìn)去了,所以可以測(cè)試所有的 Controller
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.build();
}
@Test
public void testGet() throws Exception {
//測(cè)試能夠正常獲取
Integer id = 1;
mockMvc.perform(
//發(fā)起 get 請(qǐng)求
get("/user/" + id)
)
//斷言請(qǐng)求的狀態(tài)是成功的(200)
.andExpect(status().isOk())
//斷言返回對(duì)象的 id 和請(qǐng)求的 id 相同
.andExpect(jsonPath("$.id").value(id));
}
@Test
public void listForAll() throws Exception {
//測(cè)試正常獲取
mockMvc.perform(
//發(fā)起 post 請(qǐng)求
post("/user/listForAll")
)
//斷言請(qǐng)求狀態(tài)
.andExpect(status().isOk())
//斷言返回結(jié)果是數(shù)組
.andExpect(jsonPath("$").isArray())
//斷言返回?cái)?shù)組不是空的
.andExpect(jsonPath("$").isNotEmpty());
}
}
總結(jié)
其實(shí)上面的測(cè)試類(lèi)的注解感覺(jué)都差不多,我們可以將一些普遍的注解封裝到基類(lèi),然后測(cè)試類(lèi)只要繼承基類(lèi)就能得到所需要的環(huán)境,吾輩自己的測(cè)試基類(lèi)在 src/test/common 下面,具體使用方法便留到下次再說(shuō)吧
以上代碼已全部放到 GitHub 上面,可以直接 clone 下來(lái)進(jìn)行測(cè)試
到此這篇關(guān)于如何用SpringBoot 進(jìn)行測(cè)試的文章就介紹到這了,更多相關(guān)SpringBoot 測(cè)試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解在SpringBoot中使用MongoDb做單元測(cè)試的代碼
- 使用@SpringBootTest注解進(jìn)行單元測(cè)試
- SpringBoot生產(chǎn)環(huán)境和測(cè)試環(huán)境配置分離的教程詳解
- springboot+idea+maven 多模塊項(xiàng)目搭建的詳細(xì)過(guò)程(連接數(shù)據(jù)庫(kù)進(jìn)行測(cè)試)
- Springboot文件上傳功能簡(jiǎn)單測(cè)試
- Springboot使用Junit測(cè)試沒(méi)有插入數(shù)據(jù)的原因
- Springboot測(cè)試類(lèi)沒(méi)有bean注入問(wèn)題解析
- springboot單元測(cè)試兩種方法實(shí)例詳解
- SpringBoot 單元測(cè)試JUnit的使用詳解
- SpringBoot2種單元測(cè)試方法解析
- SpringBoot Controller Post接口單元測(cè)試示例
- spring-mvc/springboot使用MockMvc對(duì)controller進(jìn)行測(cè)試
相關(guān)文章
Java SpringMVC的@RequestMapping注解使用及說(shuō)明
這篇文章主要介紹了Java SpringMVC的@RequestMapping注解使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01
Java?ArrayList實(shí)現(xiàn)班級(jí)信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java?ArrayList實(shí)現(xiàn)班級(jí)信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
Java實(shí)現(xiàn)用Mysql存取圖片操作實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)用Mysql存取圖片操作實(shí)例,本文講解了使用BLOB類(lèi)型保存和讀取圖片的代碼實(shí)例,需要的朋友可以參考下2015-06-06
Springboot整合Java?DL4J實(shí)現(xiàn)交通標(biāo)志識(shí)別系統(tǒng)全過(guò)程
在自動(dòng)駕駛系統(tǒng)中,交通標(biāo)志識(shí)別是實(shí)現(xiàn)車(chē)輛智能化的關(guān)鍵技術(shù)之一,本文介紹了利用SpringBoot和JavaDeeplearning4j構(gòu)建交通標(biāo)志識(shí)別系統(tǒng)的方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-10-10
Java?windows環(huán)境構(gòu)建圖文教程
這篇文章主要為大家介紹了Java?windows環(huán)境構(gòu)建圖文教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2023-12-12
SpringBoot快速實(shí)現(xiàn)接口消息加密的過(guò)程詳解
在項(xiàng)目中,為了保證數(shù)據(jù)的安全,我們常常會(huì)對(duì)傳遞的數(shù)據(jù)進(jìn)行加密,常用的加密算法包括對(duì)稱(chēng)加密(AES)和非對(duì)稱(chēng)加密(RSA),博主選取碼云上最簡(jiǎn)單的API加密項(xiàng)目進(jìn)行下面的講解,需要的朋友可以參考下2023-11-11

