1 前言
編寫 JAVA 單元測試用例,即把一段復雜的代碼拆解成一系列簡單的單元測試用例,并且無需啟動服務,在短時間內測試代碼中的處理邏輯。寫好 Java 單元測試用例,其實就是把 “復雜問題簡單化,建單問題深入化 “。在編寫的過程中, 我們也可以對自己的代碼進行一個二次檢查。
以下是我總結的一些編寫單元測試的好處:
1. 測試代碼邏輯時,不需要啟動整個應用。
2. 單元測試可以覆蓋邊界值
3. 提高原有代碼的復用
4. 可以有效避免代碼改動后,對原有邏輯的潛在影響
2 準備環境
Mockito 是目前最普遍的單元測試模擬框架。Mockito 可以模擬應用中依賴的復雜對象,從而把測試對象和依賴對象隔離開。PowerMock 為 Mockito 提供了擴展功能。為模擬靜態方法,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 包中已經包含了對應的 Mockito 和 JUnit 包,所以無需再單獨引入。
3 一些常用的 mock 語句
3.1 模擬指定類的對象實例,用于模擬依賴對象(類成員)
在 Spring 中,這些成員對象通過 @Autowire,@Resource,@Value 等方式注入,可能涉及到環境配置或者依賴第三方接口。在單元測試中,不是我們關注的點,所以可以用 mock 模擬
//方法一
Mockito.mock(OrderInfo.class);
//方法二
@Mock
private OrderInfo orderInfo;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
3.2 定義被測試對象
把被測試服務類進行實例化
@InjectMocks
private OrderServiceImpl orderService;
3.3 模擬枚舉類型 / 靜態方法
需要把對應的模擬類放在 @PrepareForTest 中
//必須添加@RunWith和@PrepareForTest在類前
@RunWith(PowerMockRunner.class)
@PrepareForTest(OrderTypeEnum.class)
//在@Before中添加枚舉mock
@Before
public void beforeTest() {
mockStatic(OrderTypeEnum.class);
}
3.4 模擬依賴方法
在模擬完依賴的參數和返回值后,可以利用 Mockito 功能,進行依賴方法的模擬。如果模擬對象還有方法調用,則需要模擬這些依賴對象的方法。
/***
when.thenReturn 和 doReturn.when是兩種實現方式
只有在使用@Spy時才會有區別
參考鏈接:https://www.imooc.com/wenda/detAIl/594190#id_653606
***/
//模擬枚舉的方法調用
when(OrderTypeEnum.getByValue(anyInt())).thenReturn(100);
//模擬依賴對象的依賴方法調用
doReturn(resultInfoDTO).when(orderInfoService).getLastOrderInfo(orderInfoDTO);
3.5 模擬構造方法
PowerMock 提供了對構造方法的模擬,但是需要把構造方法的類放在 @PrepareForTest 中
//必須在@PrepareForTest中添加對應類
@PrepareForTest({OrderTypeEnum.class, OrderServiceImpl.class})
whenNew(OrderInfoDTO.class).withNoArguments().thenReturn(orderInfoDTO);
3.6 驗證方法調用次數
被測方法調用后,一些方法會出現調用多次或根據不同條件進行不同次數的調用。此時,可以根據驗證方法調用次數,確定代碼的有效性
verify(orderInfoService,times(1)).getLastOrderInfo(orderInfoDTO);
3.7 驗證返回值
對于方法調用后的出參,我們會有一定的預期。所以,可以根據校驗返回值是否符合預期,確保返回值的正確性
Assert.assertEquals(result, "123");
3.8 驗證異常對象
JUnit 的 @Test 注解提供了一個 expected 屬性,可以指定一個期望的異常類型,用于捕獲異常并驗證其異常類型?!咀ⅰ浚褐荒茯炞C異常類型,不能驗證異常信息。
@Test(expected = BPLException.class)
4 單測舉例
下面是一個本地方法的單元測試用例,方法中調用了外部接口,并且其中包含了枚舉值的使用。
源方法即需要單測方法:
首先,是單元測試時一些必要的初始化:
4.1 單測場景一(確定接口調用,并返回值正確):
通過 verify 方法來確定接口是否調用過,并且只調用過 1 次。
通過 assert 來確認返回值是否滿足預期
4.2 單測場景二(必要異常是否拋出):
通過在 @Test 注解上加入 expected 屬性,測試當接口返回值為空時,是否可以拋出異常
4 總結
編寫單元測試在開發中的地位舉足輕重。在開發過程中,避免不了優化或重構歷史代碼。單元測試,在一定程度上可以幫助測試更新后邏輯,以及潛在調用鏈。另外也分享一些鏈接,希望可以幫助大家完成從 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
作者:京東物流 牟佳義
來源:京東云開發者社區 自猿其說 Tech 轉載請注明來源