Java Spring Boot實(shí)戰(zhàn)練習(xí)之單元測(cè)試篇

一、關(guān)于JUnit的一些東西
在我們開發(fā)Web應(yīng)用時(shí),經(jīng)常會(huì)直接去觀察結(jié)果進(jìn)行測(cè)試。雖然也是一種方式,但是并不嚴(yán)謹(jǐn)。作為開發(fā)者編寫測(cè)試代碼來測(cè)試自己所寫的業(yè)務(wù)邏輯是,以提高代碼的質(zhì)量、降低錯(cuò)誤方法的概率以及進(jìn)行性能測(cè)試等。經(jīng)常作為開發(fā)這寫的最多就是單元測(cè)試。引入spring-boot-starter-testSpringBoot的測(cè)試依賴。該依賴會(huì)引入JUnit的測(cè)試包,也是我們用的做多的單元測(cè)試包。而Spring Boot在此基礎(chǔ)上做了很多增強(qiáng),支持很多方面的測(cè)試,例如JPA,MongoDB,Spring MVC(REST)和Redis等。
接下來主要是測(cè)試業(yè)務(wù)邏輯層的代碼,REST和Mock測(cè)試。
1.1 JUnit介紹
JUnit是一個(gè)Java語言的單元測(cè)試框架。它由Kent Beck和Erich Gamma建立,逐漸成為源于Kent Beck的sUnit的xUnit家族中最為成功的一個(gè)。 JUnit有它自己的JUnit擴(kuò)展生態(tài)圈。多數(shù)Java的開發(fā)環(huán)境都已經(jīng)集成了JUnit作為單元測(cè)試的工具。
| JUnit相關(guān)概念 | 含義 |
|---|---|
| 測(cè)試 | 一個(gè)以@Test注釋的方法定義一個(gè)測(cè)試,運(yùn)行這個(gè)方法,JUnit會(huì)創(chuàng)建一個(gè)包含類的實(shí)例,然后再調(diào)用這個(gè)被注釋的方法。 |
| 測(cè)試類 | 包含多個(gè)@Test方法的一個(gè)類 |
| Assert | 定義想測(cè)試的條件,當(dāng)條件成立時(shí),assert 方法保持沉默,條件不成立時(shí),則拋出異常 |
| Suite | Suite允許將測(cè)試類歸類成一組 |
| Runner | Runner類用于運(yùn)行測(cè)試,JUnit4是向后兼容的,可以運(yùn)行JUnit3的測(cè)試實(shí)例 |
這里使用的是JUnit4.x版本,JUnit中有兩個(gè)重要的類Assume+Assert,以及重要的注解:BeforeClass、AfterClass、After、Before、Test和Ignore。BeforeClass和AfterClass在每個(gè)類的開始和結(jié)束的時(shí)候運(yùn)行,需要static修飾方法。而Before和After則是在每個(gè)測(cè)試方法的開始和結(jié)束的時(shí)候運(yùn)行。
代碼片段:TestDeployApplication.class是自己編寫的Spring Boot啟動(dòng)類。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {TestDeployApplication.class})
public class UnitTest1 {
@BeforeClass
public static void beforeClass() {
System.out.println("=================BeforeClass================");
}
@AfterClass
public static void afterClass() {
System.out.println("=================AfterClass================");
}
@Before
public void beforeTest() {
System.out.println("before test");
}
@After
public void afterTest() {
System.out.println("after test");
}
@Test
public void test1() {
System.out.println("test1");
}
@Test
public void test2() {
System.out.println("test2");
}
}
1.2 JUnit的Assert類
Assert類中常用的方法:
- assertEquals(“提示信息”,A,B):當(dāng)判斷A是否等于B,不等于就拋出錯(cuò)誤。比較對(duì)象是調(diào)用的是equals()方法。
- assertSame(“提示信息”,A,B):判斷對(duì)象是否相同。
- assertTrue(“提示信息”,A):判斷條件A是否為真。
- assertFalse(“提示信息”,A):判斷條件是否為假。
- assertNotNull(“提示信息”,A):判斷對(duì)象是否不為空。
- assertNull(“提示信息”,A):判斷對(duì)象是否不為空。
- assertArrayEqual(“提示信息”,A,B):判斷數(shù)組A和數(shù)組B是否相等。
1.3 JUnit的Suite
JUnit的Suite設(shè)計(jì)就是一次性運(yùn)行一個(gè)或多個(gè)測(cè)試用例,Suite可以看作是一個(gè)容器,用來把測(cè)試類歸類在一起,并把他們作為一個(gè)集合來運(yùn)行,運(yùn)行器啟動(dòng)Suite。
@RunWith(Suite.class)
@SuiteClasses({UnitTest1.class,UnitTest2.class})
public class MainTest{
}
二、Spring Boot單元測(cè)試
添加需要的依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2.1 Spring Boot測(cè)試依賴提供的測(cè)試范圍
引入了spring-boot-starter-test繼承了很多的測(cè)試庫:
- JUnit,標(biāo)準(zhǔn)的單元測(cè)試Java程序。
- Spring Test和Spring Boot Test,對(duì)Spring Boot應(yīng)用的單元測(cè)試。
- Mockito,Java Mock測(cè)試框架,用于模擬任何Spring管理的Bean。例如在- - 單元測(cè)試中,模擬一個(gè)第三方系統(tǒng)接口返回的數(shù)據(jù),而不用真正地去請(qǐng)求第三方接口。
- AssertJ,一個(gè)assertion庫,同時(shí)提供了更加多的期望值與測(cè)試返回值的比較方式。
- Hamcrest,庫的匹配對(duì)象。
- JSONassert,對(duì)JSON對(duì)象或者JSON字符串?dāng)嘌缘膸臁?/li>
- JSONPath,提供向XPath那樣的符號(hào)來獲取JSON字段。
2.2 Spring Boot單元測(cè)試的腳手架
在使用spring.io創(chuàng)建的Spring Boot工程中,就默認(rèn)常見了一個(gè)單元測(cè)試的類。
@RunWith(SpringRunner.class)
@SpringBootTest
public class UnitTest1 {
@Test
public void contextLoads(){
}
}
@RunWith是JUnit中的注解,用來通知JUnit單元測(cè)試框架不要使用內(nèi)置的方式進(jìn)行單元測(cè)試,向上面的寫法,就是指定使用SpringRunner類來提供單元測(cè)試。
@SpringBootTest注解則是用于Spring Boot應(yīng)用的測(cè)試,默認(rèn)會(huì)分局報(bào)名逐級(jí)往上查找Spring Boot主程序,也就是@SpringBootApplocation注解,并在單元測(cè)試啟動(dòng)的時(shí)候啟動(dòng)該類來創(chuàng)建Spring上下文。所以我們?cè)趯?duì)Spring Boot應(yīng)用進(jìn)行單元測(cè)試的時(shí)候,在日志輸出都可以看到Spring Boot應(yīng)用的啟動(dòng)日志。
2.3 對(duì)Service層代碼測(cè)試
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.*;
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class ServiceUnitTest {
@MockBean
private ThirdSystemService thirdSystemService;
@Autowired
private ISysUserService userService;
@Test
public void test1() {
Long expectResult = 100L;
given(thirdSystemService.develop()).willReturn(expectResult);
SysUser sysUser = userService.findById(expectResult);
System.out.println(sysUser.toString());
}
}
@MockBean可以獲取在Spring下上文管理的Bean,但是thirdSystemService這個(gè)Bean并不是真的實(shí)列,而是通過Mockito工具創(chuàng)建的測(cè)試實(shí)例。通過@MockBean注解模擬出來的Bean,調(diào)用方法是不會(huì)真正的調(diào)用真正的方法,適用于在依賴了第三方的系統(tǒng),然而第三方的系統(tǒng)的對(duì)接并沒有實(shí)現(xiàn)完成,自己可以單獨(dú)測(cè)試自己的業(yè)務(wù)代碼。willReturn(expectResult)說明結(jié)果永遠(yuǎn)返回100L。
2.4 測(cè)試MVC代碼
Spring Boot中還能單獨(dú)測(cè)試Controller的代碼,例如測(cè)試Controller中方法的參數(shù)綁定和校驗(yàn)之類的邏輯??梢酝ㄟ^@WebMvcTest注解來完成單元測(cè)試。
@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {
@Autowired
private MockMvc mockMvc;
@Test
public void test2() throws Exception {
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/hello/{id}", 1L);
mockMvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
}
像Get方法傳遞參數(shù)
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/hello/{id}", 1L) // path變量
.param("name", "hello"); // @RequestParam 獲取變量。post請(qǐng)求也適用
文件上傳
@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {
@Autowired
private MockMvc mockMvc;
@Test
public void test3() throws Exception {
// 獲取文件
FileInputStream fileInputStream = new FileInputStream("文件路徑");
// 構(gòu)建文件上傳對(duì)象
MockMultipartFile mockMultipartFile = new MockMultipartFile("file", fileInputStream);
// 構(gòu)建mock文件上傳請(qǐng)求
MockMultipartHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.multipart("/upload").file(mockMultipartFile);
// 發(fā)送請(qǐng)求
mockMvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
}
模擬Cookie和Session
@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {
@Autowired
private MockMvc mockMvc;
@Test
public void test4() throws Exception {
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
.get("index.html")
.sessionAttr("name", "hello")
.cookie(new Cookie("token", "123345"));
mockMvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
}
設(shè)置請(qǐng)求頭
@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {
@Autowired
private MockMvc mockMvc;
@Test
public void test5() throws Exception {
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
.get("index.html")
.content(MediaType.APPLICATION_JSON_VALUE) // 期望返回類型
.contentType(MediaType.APPLICATION_JSON_VALUE) // 提交的內(nèi)容類型
.header("token", 1235); // 設(shè)置請(qǐng)求頭
mockMvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
}
2.5 比較返回結(jié)果
MockMvc類的perform方法會(huì)返回一個(gè)ResultAction類,可以對(duì)結(jié)果進(jìn)行一些操作(andExpect、andDo和andReturn)。
@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {
@Autowired
private MockMvc mockMvc;
@Test
public void test2() throws Exception {
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/hello/{id}", 1L)
.param("name", "hello");
mockMvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.jsonPath("$.id", "id").value(2L));
.andDo(MockMvcResultHandlers.print());
}
}
例如上面獲取返回的JSON結(jié)果中的id字段的值,value是期望值,如果期望值與實(shí)際值不一樣測(cè)試就會(huì)報(bào)錯(cuò)。
也可以斷言測(cè)試返回結(jié)果的View(視圖)和Model(數(shù)據(jù)模型)是否是期望值
@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {
@Autowired
private MockMvc mockMvc;
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/hello/{id}", 1L)
.param("name", "hello");
mockMvc.perform(requestBuilder)
// 斷言返回的試圖
.andExpect(MockMvcResultMatchers.view().name("index.html"))
// 斷言返回的數(shù)據(jù)模型中的數(shù)據(jù)
.andExpect(MockMvcResultMatchers.model().attribute("id",1L))
.andDo(MockMvcResultHandlers.print());
}
更多的結(jié)果斷言可以在MockMvcResultMatchers類中找到,該類是請(qǐng)求結(jié)果的匹配的一個(gè)工具類。
如果對(duì)軟件測(cè)試、接口測(cè)試、自動(dòng)化測(cè)試、持續(xù)集成、面試經(jīng)驗(yàn)。感興趣 可以進(jìn)到806549072,群內(nèi)會(huì)有不定期的分享測(cè)試資料。還會(huì)有技術(shù)大牛,業(yè)內(nèi)同行一起交流技術(shù)
到此這篇關(guān)于Java Spring Boot實(shí)戰(zhàn)練習(xí)之單元測(cè)試篇的文章就介紹到這了,更多相關(guān)Java Spring Boot 單元測(cè)試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Spring @DependsOn控制bean加載順序的實(shí)例
這篇文章主要介紹了使用Spring @DependsOn控制bean加載順序的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
ShardingSphere結(jié)合MySQL實(shí)現(xiàn)分庫分表的項(xiàng)目實(shí)踐
在實(shí)際開發(fā)中,如果表的數(shù)據(jù)過大我們需要把一張表拆分成多張表,本文主要介紹了使用ShardingSphere實(shí)現(xiàn)MySQL分庫分表,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
使用BigDecimal進(jìn)行精確運(yùn)算(實(shí)現(xiàn)加減乘除運(yùn)算)
這篇文章主要介紹了如何使用BigDecimal進(jìn)行精確運(yùn)算,最后提供了一個(gè)工具類,該工具類提供加,減,乘,除運(yùn)算2013-11-11
Java中基于Nacos實(shí)現(xiàn)Sentinel規(guī)則持久化詳解
這篇文章主要介紹了Java中基于Nacos實(shí)現(xiàn)Sentinel規(guī)則持久化詳解,Sentinel Dashboard中添加的規(guī)則數(shù)據(jù)存儲(chǔ)在內(nèi)存,微服務(wù)停掉規(guī)則數(shù)據(jù)就消失,在?產(chǎn)環(huán)境下不合適,我們可以將Sentinel規(guī)則數(shù)據(jù)持久化到Nacos配置中?,讓微服務(wù)從Nacos獲取規(guī)則數(shù)據(jù),需要的朋友可以參考下2023-09-09
java設(shè)計(jì)模式之觀察者模式簡(jiǎn)單解讀
這篇文章主要介紹了java設(shè)計(jì)模式之觀察者模式簡(jiǎn)單解讀,觀察者模式是在對(duì)象之間定義了一對(duì)多的依賴,這樣一來,當(dāng)一個(gè)對(duì)象改變狀態(tài),依賴它的對(duì)象會(huì)收到通知并自動(dòng)更新,需要的朋友可以參考下2023-10-10
使用IDEA啟動(dòng)項(xiàng)目遇見ClassNotFoundException的解決方案
這篇文章主要介紹了使用IDEA啟動(dòng)項(xiàng)目遇見ClassNotFoundException的正確解決方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
詳解Spring Boot最新版優(yōu)雅停機(jī)的方法
這篇文章主要介紹了Spring Boot最新版優(yōu)雅停機(jī)的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
SpringBoot同時(shí)啟動(dòng)不同端口圖示解析
這篇文章主要介紹了SpringBoot同時(shí)啟動(dòng)不同端口圖示解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02

