編寫Java單元測試用例,即把一段複雜的程式碼拆解成一系列簡單的單元測試用例,並且無需啟動服務,在短時間內測試程式碼中的處理邏輯。寫好Java單元測試用例,其實就是把「複雜問題簡單化,建單問題深入化「。在編寫的過程中, 我們也可以對自己的程式碼進行一個二次檢查。
以下是我總結的一些編寫單元測試的好處:
1.測試程式碼邏輯時,不需要啟動整個應用。
2.單元測試可以覆蓋邊界值
3.提高原有程式碼的複用
4.可以有效避免程式碼改動後,對原有邏輯的潛在影響
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.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屬性,可以指定一個期望的異常型別,用於捕獲異常並驗證其異常型別。【注】:只能驗證異常型別,不能驗證異常資訊。
@Test(expected = BPLException.class)
下面是一個本地方法的單元測試用例,方法中呼叫了外部介面,並且其中包含了列舉值的使用。
源方法即需要單測方法:
首先,是單元測試時一些必要的初始化:
4.1 單測場景一(確定介面呼叫,並返回值正確):
通過verify方法來確定介面是否呼叫過,並且只呼叫過1次。
通過assert來確認返回值是否滿足預期
4.2 單測場景二(必要異常是否丟擲):
通過在@Test註解上加入expected屬性,測試當介面返回值為空時,是否可以丟擲異常
編寫單元測試在開發中的地位舉足輕重。在開發過程中,避免不了優化或重構歷史程式碼。單元測試,在一定程度上可以幫助測試更新後邏輯,以及潛在呼叫鏈。另外也分享一些連結,希望可以幫助大家完成從0到1的搭建。
作者:京東物流 牟佳義
來源:京東雲開發者社群 自猿其說Tech 轉載請註明來源