Spring Boot+JUnit5+Mockito單元測試
導(dǎo)語:
最近領(lǐng)導(dǎo)要求項目添加單元測試妻怎,指定用
JUnit5
和Mockito
,之前沒玩過這兩個東西扁瓢,這幾天在網(wǎng)上查了很多資料,略懂皮毛,這篇文章打算把這些東西整理出來方淤。
一,單元測試和JUnit
單元測試
要玩這個東西蹄殃,首先得知道什么是單元測試携茂,這個很重要,我之前一直糾結(jié)寫單元測試的意義在哪诅岩。
單元測試就是針對最小的功能單元編寫測試代碼讳苦,Java程序最小的功能單元就是方法,因此吩谦,對Java程序進行單元測試就是針對單個Java方法進行測試鸳谜。
先了解一下什么是測試驅(qū)動開發(fā)
所謂測試驅(qū)動開發(fā),是指先編寫接口式廷,緊接著編寫測試咐扭。編寫完測試后,我們才開始真正編寫實現(xiàn)代碼滑废。在編寫實現(xiàn)過程中蝗肪,我們一邊寫一邊測,什么時候測試全部通過了蠕趁,就表示編寫的實現(xiàn)完成了薛闪。而當(dāng)接口需要修改時,修改完只要跑一遍單元測試俺陋,如果過了豁延,說明修改沒有問題。
然而這是一種理想的情況腊状,現(xiàn)實中大部分情況都是我們已經(jīng)寫好了實現(xiàn)的代碼诱咏,需要對已有的代碼進行測試。
在沒有用JUnit之前我們通常會使用main()
來運行測試代碼寿酌,接口實現(xiàn)寫完了胰苏,用main()
跑一遍,但是使用main()
有很多缺點醇疼,一是不能把測試代碼分離硕并,二是main()
只能有一個,三是main()
是靜態(tài)的會影響很多測試秧荆,誰用誰知道倔毙。
所以我們需要了解一下JUnit
框架。
JUnit框架
JUnit是一個開源的Java語言的單元測試框架乙濒,專門針對Java設(shè)計陕赃,使用最廣泛卵蛉。JUnit目前最新版本是JUnit5。
關(guān)于JUnit的使用這里就不寫了么库,網(wǎng)上教程一大把傻丝,貼上個比較完整的教程JUnit教程
Maven依賴
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
二,Mockito框架
既然是對程序中的某個方法進行測試诉儒,難免會遇到方法中有外部依賴的時候葡缰,這時候就可以用Mockito來進行模擬外部依賴,比如一個controller中有service調(diào)用忱反,這時就可以用Mockito把這個Service對象Mock掉泛释,真正的實現(xiàn)"單元"測試。
關(guān)于Mockito使用温算,這里也貼上教程Mockito教程
三怜校,Controller層測試示例
了解完這兩個東西就可以使用他們來測試代碼了,首先看下Controller層注竿,在Controller層中我們的每個方法都是一個HTTP請求的入口茄茁,所以我們要測試Controller代碼需要模擬HTTP請求,使用MockMvc
就可以做到蔓搞。
MockMvc的使用方式:
- MockMvcBuilder構(gòu)造MockMvc的構(gòu)造器
- MockMvcRequestBuilders創(chuàng)建請求request
- mockMvc調(diào)用perform胰丁,執(zhí)行一個request請求,調(diào)用controller的業(yè)務(wù)處理邏輯喂分,返回ResultActions
- 可以通過ResultActions, MockMvcResultMatchers對結(jié)果進行驗證
示例一(GET請求不帶參數(shù)):
@SpringBootTest()
@Slf4j
public class MockMvcDemo extends AbstractBaseTest {
private MockMvc mvc;
@BeforeEach
public void setUp() {
// (1)構(gòu)建mvc環(huán)境
mvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
}
@Test
public void test() throws Exception {
// (2)構(gòu)建請求
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/account/info")
.contentType("text/html")
.accept(MediaType.APPLICATION_JSON);
// (3)發(fā)送請求锦庸,獲取請求結(jié)果
ResultActions perform = mvc.perform(request);
// (4)請求結(jié)果校驗
perform.andExpect(MockMvcResultMatchers.status().isOk());
MvcResult mvcResult = perform.andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
// (5)校驗返回信息
log.info(response.getContentAsString());
}
}
示例二(POST請求帶token和參數(shù)對象):
如果是POST請求帶參數(shù)對象,可以使用MockMvcRequestBuilders.post方法模擬POST請求
@SpringBootTest()
@Slf4j
public class MockMvcDemo extends AbstractBaseTest {
private MockMvc mvc;
@BeforeEach
public void setUp() {
// (1)構(gòu)建mvc環(huán)境
mvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
}
@Test
public void test() throws Exception {
// (2)構(gòu)建請求
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.post("/account/info")
.header("Authorization",token)
.contentType(MediaType.APPLICATION_JSON)
.content(JSON.toJSONString(params));
// (3)發(fā)送請求蒲祈,獲取請求結(jié)果
ResultActions perform = mvc.perform(request);
// (4)請求結(jié)果校驗
perform.andExpect(MockMvcResultMatchers.status().isOk());
MvcResult mvcResult = perform.andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
// (5)校驗返回信息
log.info(response.getContentAsString());
}
}
示例三(使用Mockito模擬掉外部依賴):
Controller中一般都會依賴其他Service服務(wù)甘萧,可以使用Mockito模擬掉,只專注于Controller層的測試梆掸,假設(shè)AccountController依賴于AccountService中的service方法,參考以下示例:
@SpringBootTest()
@Slf4j
public class MockMvcDemo extends AbstractBaseTest {
private MockMvc mvc;
@Mock
private AccountService service;
@InjectMocks
private AccountController controller;
@BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);//這句話執(zhí)行以后扬卷,service自動注入到controller中。
// (1)構(gòu)建mvc環(huán)境
mvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void test() throws Exception {
// (2)模擬外部依賴返回結(jié)果
when(service.service(Mockito.any(Param.class))).thenReturn("success");
// (3)構(gòu)建請求
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.post("/account/info")
.header("Authorization",token)
.contentType(MediaType.APPLICATION_JSON)
.content(JSON.toJSONString(params));
// (4)發(fā)送請求酸钦,獲取請求結(jié)果
ResultActions perform = mvc.perform(request);
// (5)請求結(jié)果校驗
perform.andExpect(MockMvcResultMatchers.status().isOk());
MvcResult mvcResult = perform.andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
// (6)校驗返回信息
log.info(response.getContentAsString());
}
}
注意:
有些依賴沒法用@InjectMocks來自動注入怪得,可以通過引入ReflectionTestUtils,解決依賴注入的問題卑硫。
ReflectionTestUtils.setField(controller,"service",service);