1 前言
編寫Java單元測試用例,即把一段復(fù)雜的代碼拆解成一系列簡單的單元測試用例,并且無需啟動服務(wù),在短時間內(nèi)測試代碼中的處理邏輯。寫好Java單元測試用例,其實(shí)就是把“復(fù)雜問題簡單化,建單問題深入化“。在編寫的過程中, 我們也可以對自己的代碼進(jìn)行一個二次檢查。
以下是我總結(jié)的一些編寫單元測試的好處:
1.測試代碼邏輯時,不需要啟動整個應(yīng)用。
2.單元測試可以覆蓋邊界值
3.提高原有代碼的復(fù)用
4.可以有效避免代碼改動后,對原有邏輯的潛在影響
2 準(zhǔn)備環(huán)境
Mockito是目前最普遍的單元測試模擬框架。Mockito可以模擬應(yīng)用中依賴的復(fù)雜對象,從而把測試對象和依賴對象隔離開。PowerMock為Mockito提供了擴(kuò)展功能。為模擬靜態(tài)方法,final類,和私有方法等。我們選擇使用以Mockito為主,PowerMock為輔的框架來做單元測試。
2.1 引入Mockito和PowerMock包,在pom.xml文件中加入以下依賴:
<properties>
<powermock.version>2.0.9</powermock.version>
</properties>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
PowerMock目前最新版本為2.0.9【PowerMock鏈接】由于PowerMock包中已經(jīng)包含了對應(yīng)的Mockito和JUnit包,所以無需再單獨(dú)引入。
3 一些常用的mock語句
3.1 模擬指定類的對象實(shí)例,用于模擬依賴對象(類成員)
在Spring中,這些成員對象通過@Autowire,@Resource,@Value等方式注入,可能涉及到環(huán)境配置或者依賴第三方接口。在單元測試中,不是我們關(guān)注的點(diǎn),所以可以用mock模擬
//方法一
Mockito.mock(OrderInfo.class);
//方法二
@Mock
private OrderInfo orderInfo;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
3.2 定義被測試對象
把被測試服務(wù)類進(jìn)行實(shí)例化
@InjectMocks
private OrderServiceImpl orderService;
3.3 模擬枚舉類型/靜態(tài)方法
需要把對應(yīng)的模擬類放在@PrepareForTest中
//必須添加@RunWith和@PrepareForTest在類前
@RunWith(PowerMockRunner.class)
@PrepareForTest(OrderTypeEnum.class)
//在@Before中添加枚舉mock
@Before
public void beforeTest() {
mockStatic(OrderTypeEnum.class);
}
3.4 模擬依賴方法
在模擬完依賴的參數(shù)和返回值后,可以利用Mockito功能,進(jìn)行依賴方法的模擬。如果模擬對象還有方法調(diào)用,則需要模擬這些依賴對象的方法。
/***
when.thenReturn 和 doReturn.when是兩種實(shí)現(xiàn)方式
只有在使用@Spy時才會有區(qū)別
參考鏈接:https://www.imooc.com/wenda/detail/594190#id_653606
***/
//模擬枚舉的方法調(diào)用
when(OrderTypeEnum.getByValue(anyInt())).thenReturn(100);
//模擬依賴對象的依賴方法調(diào)用
doReturn(resultInfoDTO).when(orderInfoService).getLastOrderInfo(orderInfoDTO);
3.5 模擬構(gòu)造方法
PowerMock提供了對構(gòu)造方法的模擬,但是需要把構(gòu)造方法的類放在@PrepareForTest中
//必須在@PrepareForTest中添加對應(yīng)類
@PrepareForTest({OrderTypeEnum.class, OrderServiceImpl.class})
whenNew(OrderInfoDTO.class).withNoArguments().thenReturn(orderInfoDTO);
3.6 驗(yàn)證方法調(diào)用次數(shù)
被測方法調(diào)用后,一些方法會出現(xiàn)調(diào)用多次或根據(jù)不同條件進(jìn)行不同次數(shù)的調(diào)用。此時,可以根據(jù)驗(yàn)證方法調(diào)用次數(shù),確定代碼的有效性
verify(orderInfoService,times(1)).getLastOrderInfo(orderInfoDTO);
3.7 驗(yàn)證返回值
對于方法調(diào)用后的出參,我們會有一定的預(yù)期。所以,可以根據(jù)校驗(yàn)返回值是否符合預(yù)期,確保返回值的正確性
Assert.assertEquals(result, "123");
3.8 驗(yàn)證異常對象
JUnit的@Test注解提供了一個expected屬性,可以指定一個期望的異常類型,用于捕獲異常并驗(yàn)證其異常類型。【注】:只能驗(yàn)證異常類型,不能驗(yàn)證異常信息。
@Test(expected = BPLException.class)
4 單測舉例
下面是一個本地方法的單元測試用例,方法中調(diào)用了外部接口,并且其中包含了枚舉值的使用。
源方法即需要單測方法:
首先,是單元測試時一些必要的初始化:
4.1 單測場景一(確定接口調(diào)用,并返回值正確):
通過verify方法來確定接口是否調(diào)用過,并且只調(diào)用過1次。
通過assert來確認(rèn)返回值是否滿足預(yù)期
4.2 單測場景二(必要異常是否拋出):
通過在@Test注解上加入expected屬性,測試當(dāng)接口返回值為空時,是否可以拋出異常
4 總結(jié)
編寫單元測試在開發(fā)中的地位舉足輕重。在開發(fā)過程中,避免不了優(yōu)化或重構(gòu)歷史代碼。單元測試,在一定程度上可以幫助測試更新后邏輯,以及潛在調(diào)用鏈。另外也分享一些鏈接,希望可以幫助大家完成從0到1的搭建。
5 參考資料
- Java編程技巧之單元測試用例編寫流程:https://mp.weixin.qq.com/s/hX_RIYs-nBnqVwdq5B4rhg
- powerMock的Git鏈接:https://github.com/powermock/powermock
- powerMock簡介:https://www.baeldung.com/intro-to-powermock
- 避免Install的時候Skip test: https://maven.apache.org/plugins-archives/maven-surefire-plugin-2.12.4/examples/skipping-test.html
作者:京東物流?牟佳義
來源:京東云開發(fā)者社區(qū) 自猿其說Tech 轉(zhuǎn)載請注明來源