背景
有如下AppUtils類的getAppName方法需要做單元測試:
public class AppUtils {
private static String appName = "";
public static String getAppName() {
if ("".equals(appName)) {
return "app_" + System.currentTimeMillis();
}
return appName;
}
}
假設,現在測試代碼中我用來判斷用例是否通過的條件爲調用getAppName方法的返回值是否與某個確定的字符串相等。But,在getAppName方法的內部實現中可以看到返回值的一部分是由System.currentTimeMillis的值組成,而這個值認爲不是一個恆定值,在測試中,我期望調用System.currentTimeMillis()方法時返回一個恆定值排除掉這個干擾因素。而System.currentTimeMillis的源碼如下:
public static native long currentTimeMillis();
那麼我的做法就是mock這個方法的返回值。
p.s. :github的維基上是有這個介紹的,我這裏就用我的這個示例具體進行說明,有興趣可以點擊這裏查看維基的示例。
下面根據測試代碼進行說明:
實現
@RunWith(PowerMockRunner.class) //1.
@PrepareForTest({AppUtils.class}) //2.
public class AppUtilsTest {
@Before
public void setup() {
PowerMockito.mockStatic(System.class);//3
}
@Test
public void testGetAppName() {
Long currentTimeMillis = System.currentTimeMillis();//4.
PowerMockito.when(System.currentTimeMillis()).thenReturn(currentTimeMillis);//5.
assertEquals("app_" + currentTimeMillis, AppUtils.getAppName());
}
}
maven的依賴可以參考我的 Mockito配合powermock工具mock構造函數這篇上的說明。
注意看上面測試代碼後面註釋的1、2、3、4、5。按這個順序講一下步驟:
1. 類上使用註解@RunWith(PowrMockRunner.class)
2. 類上使用的註解@PrepareFotTest括號裏的值聲明要處理的類是AppUtils.class,注意這裏是調用系統類的類AppUtils,而不是系統類System,雖然我們要mock的是System.currentTimeMillis()方法返回值。
3. 調用mockStatic方法mock System這個類
4. 這一步不主要,但是後面需要解釋下。我這裏是保存一個返回值變量。其實在這裏調用System.currentTimeMillis()方法的返回已經是默認值0了,也就是說變量currentTimeMillis的值是0,也算是個恆定值了達到我們預期了,當然這裏可以直接定義一個常量。
5. 設置調用System.currentTimeMillis()方法時的期望返回值。
其實重要的是上面的1、2、3、5步。按這幾步操作類似System.currentTimeMillis()這些方法便都可以mock它們的返回值了。
下面解釋下第2步和第4步。
第2步,我們在@PrepareFotTest註解裏聲明要處理的類是AppUtils,這是因爲Powermock在mock像System.currentTimeMillis()這種系統類的方法時實際修改的是調用這個方法的類的字節碼,而不是System類的。
第4步中,我們調用System.currentTimeMillis()方法返回值是0,是因爲在@Before註解的方法裏我mockStatic了System類,調用方法時返回了默認值。關於@Before註解的使用介紹可以看JUnit4的github上的維基,地址在這裏:https://github.com/junit-team/junit4/wiki/Test-fixtures