最近在對自己的項目寫一些單元測試,選擇了Powermock測試框架。
關於Powermock的使用網上有很多例子,這里不再介紹。但是開發代碼中有很多地方使用了接口,搜了百度和谷歌都沒有找到解決辦法。
如題,這里介紹使用Powermock對接口進行測試的一種方法,特做記錄。
待測試類和方法:這里以流行的OkHttpClient網絡請求接口Callback為例
public class DownloadResource { public void downloadResource(String url){ url += "/"; Callback callback = new Callback() { @Override public void onFailure(Call call, IOException e) { System.out.print("onFailure"); //do something. doOtherMethod(false); } @Override public void onResponse(Call call, Response response) throws IOException { System.out.print("onResponse"); //do something. doOtherMethod(true); } }; //從緩存中獲取一個httpClient實例 OkHttpClient httpClient = PafPluginCache.getInstance().getHttpClient(); Request request = new Request.Builder().url(url).get().build(); httpClient.newCall(request).enqueue(callback); } private void doOtherMethod(boolean b) { } }
對應的測試類:
@RunWith(PowerMockRunner.class) @PrepareForTest({DownloadResource.class, PafPluginCache.class}) public class DownloadResourceTest { @Test public void downloadResource() throws Exception { DownloadResource downloadResource = new DownloadResource(); DownloadResource spy = spy(downloadResource); OkHttpClient okHttpClient = mock(OkHttpClient.class); PafPluginCache pafPluginCache = mock(PafPluginCache.class); mockStatic(PafPluginCache.class); when(PafPluginCache.getInstance()).thenReturn(pafPluginCache); when(pafPluginCache.getHttpClient()).thenReturn(okHttpClient); Request.Builder builder = mock(Request.Builder.class); when(builder.url(anyString())).thenReturn(builder); when(builder.get()).thenReturn(builder); Call call = mock(Call.class); when(okHttpClient.newCall(any(Request.class))).thenReturn(call); //替換mock的method的實現 doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); System.out.print("proxy:" + invocation.getMock() + "\n"); System.out.print("invoke(" + args[0] + /*", " + args[1] +*/ ")\n"); Object o1 = args[0]; if(o1 instanceof Callback){ Callback callback = (Callback) o1; callback.onResponse(null, null); } return null; } }).when(call).enqueue(any(Callback.class)); spy.downloadResource("http://www.abc.com"); verifyPrivate(spy, times(1)).invoke("doOtherMethod", true); } }
執行測試用例的結果:

簡單總結一下原理,利用doAnswer方法獲取到被測類中被測方法的入參,其中一個入參就是接口的實例DownloadResource$1@4a83a74a。然后我們就可以調用該接口實例的方法了。
另外,准備了一些對於Powermock的使用的個人經驗,如有錯誤,歡迎指正。
- 通過mock(class)得到的mock對象,其所有非靜態方法和域都是虛假的,如果要使用這些方法和域,需要先進行設置
- mockStatic(class)后,該類的所有靜態方法和靜態域都是虛假的,且需要加入到@PrepareForTest中,如果要使用這些方法和域,需要先進行設置
- android包下的所有類都自動被mock了,且無法通過callRealMethod方法使其真實,也包括org.json下的類。
- spy(object)中入參應為真實對象,和mock對象相反,其所有非靜態方法和域都是真實的,可以對某個方法進行mock設置
- 使用verify系列方法可以驗證mock對象和spy對象的方法執行次數
- 設置when系列方法進行mock條件時,可以傳入具體值或者模糊值,但是同時傳入具體值和模糊值時,具體值需要使用eq方法包裝一下。調用方法時不可傳入模糊值
- reset(mock)方法可以重置所有mock條件和verify系列方法的驗證次數
- 調用私有方法可以使用Whitebox.invokeMethod系列方法
- suppress方法可以跳過任意一個方法,一般測試子類時,禁用父類方法。
- 使用inOrder方法來驗證mock對象的調用順序
