Java單元測試及常用語句

2023-08-31 12:00:25

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屬性,可以指定一個期望的異常型別,用於捕獲異常並驗證其異常型別。【注】:只能驗證異常型別,不能驗證異常資訊。

@Test(expected = BPLException.class)

4 單測舉例

下面是一個本地方法的單元測試用例,方法中呼叫了外部介面,並且其中包含了列舉值的使用。

源方法即需要單測方法:

首先,是單元測試時一些必要的初始化

4.1 單測場景一(確定介面呼叫,並返回值正確):

通過verify方法來確定介面是否呼叫過,並且只呼叫過1次。

通過assert來確認返回值是否滿足預期

4.2 單測場景二(必要異常是否丟擲):

通過在@Test註解上加入expected屬性,測試當介面返回值為空時,是否可以丟擲異常

4 總結

編寫單元測試在開發中的地位舉足輕重。在開發過程中,避免不了優化或重構歷史程式碼。單元測試,在一定程度上可以幫助測試更新後邏輯,以及潛在呼叫鏈。另外也分享一些連結,希望可以幫助大家完成從0到1的搭建。

5 參考資料

作者:京東物流 牟佳義

來源:京東雲開發者社群 自猿其說Tech 轉載請註明來源