代碼出處:colin-phang/AndroidUnitTesthtml
上篇文章:Android單元測試在複雜項目裏的落地姿式(調研篇)java
上篇《調研》的結論是:android
- Espresso須要跑在真機上,可用於依賴Android平臺的功能測試。
- Roboelctric問題太多在複雜項目中步履維艱,棄了。
- 考慮PowerMockito來隔離整個Android SDK以及項目業務的依賴,來保證單元測試代碼可以快速有效地編寫並執行。
文章主要分紅 調研、 實踐 兩篇。 本篇主要講講基於PowerMockito如何在項目進行Android單元測試的實踐。git
參考:powermock/wikigithub
testImplementation 'junit:junit:4.12' testImplementation "org.powermock:powermock-module-junit4:2.0.2" testImplementation "org.powermock:powermock-api-mockito2:2.0.2" 複製代碼
按照上述引入PowerMock的依賴後便可在項目test目錄下使用PowerMockito和Mockito了。api
@RunWith(PowerMockRunner.class) @PrepareForTest( { YourClassWithEgStaticMethod.class }) public class YourTestCase { ... } 複製代碼
上一篇提到,結合PowerMockito編寫單元測試代碼,遵循如下三個步驟:markdown
而單元測試用例的編寫,一部分取決於對業務代碼的熟悉程度,另外一方面則取決於對單元測試框架的瞭解程度,如下框架的不少用法具體仍是須要本身去搜索資料並掌握的, 具體能夠參考這兩個文檔:框架
上篇文章也有一個簡單的示例:PowerMockito在Android單元測試中的簡單使用,這裏再也不贅述,下面說說在編寫單元測試代碼過程當中,如何藉助PowerMockito隔離Android SDK的依賴。函數
activity = PowerMockito.mock(new MainActivity()) //使activity的isFinishing方法老是返回true when(activity.isFinishing()).thenReturn(true); 複製代碼
經過mock創造出來的對象,調用該對象全部方法都不會執行真實邏輯。必須結合when(...).then(...)
來使模擬對象按照咱們預期返回。oop
activity = PowerMockito.spy(new MainActivity()) //使activity的isFinishing方法老是返回false PowerMockito.doReturn(false).when(activity).isFinishing(); 複製代碼
經過spy創造模擬對象必須先手動new出來,調用該對象全部方法都會執行真實邏輯。 spy對象必須結合doReturn(...).when(...)
纔會忽略真實邏輯,並按照咱們預期返回。
若是函數返回值爲void,能夠用
doNothing()
代替doReturn()
。
有時候被測類絕大部分是private函數(好比Activity),傳統的單元測試很難覆蓋到這些private函數,固然咱們能夠經過重構/封裝使咱們的業務代碼對測試更友好,但爲了測試而對本來穩定的業務代碼進行侵入式的修改,在短時間內確定會帶來不穩定因素,這每每是團隊/領導沒法容忍的。
PowerMock的Whitebox
類提供了一組api能夠獲取/修改private的變量和函數,能夠幫助咱們繞太重構去對業務代碼進行測試。
//修改私有變量 Whitebox.setInternalState(..) //訪問私有變量 Whitebox.getInternalState(..) //調用私有函數 Whitebox.invokeMethod(..) //調用私有的構造函數 Whitebox.invokeConstructor(..) 複製代碼
非靜態內部類的對象會隱式持有外部類對象,因此mock非靜態內部類,須要給」this$0「的成員變量賦值,否則單元測試代碼運行時會報錯。
Whitebox.setInternalState(innerObj, "this$0", outerObj) 複製代碼
在實際項目中會有不少經常使用但不影響業務邏輯的代碼(Log以及其餘統計代碼等等),有些靜態代碼塊也直接調用Android SDK api。由於單元測試代碼運行在JVM上,這些代碼很容易會報錯,若是爲了測試去修改這些代碼未免有點本末倒置,因此咱們在單元測試的過程當中須要抑制/隔離這些代碼的執行。
PowerMockito提供了@SuppressStaticInitializationFor
註解:
//在單元測試類以前聲明如下註解,能夠阻止FileUtil類的靜態代碼塊運行 @SuppressStaticInitializationFor("com.colin.unittest.FileUtil") public class PowerMockitoSampleIII { ... } 複製代碼
藉助mockStatic
可使指定類的靜態方法不執行。
@PrepareForTest(Log.class) public class PowerMockitoSampleIII { @Before public void setUp() throws Exception { //抑制Log相關代碼的執行 PowerMockito.mockStatic(Log.class); } ... } 複製代碼
實際業務開發中,咱們常常須要繼承Android SDK的類來進行擴展,對這些類覆寫的函數進行單元測試時,每每須要抑制父類super()的邏輯,否則在JVM中執行單元測試代碼時會報錯。
//抑制MainActivity父類的onDestroy方法 Method method = PowerMockito.method(MainActivity.class.getSuperclass(), "onDestroy"); PowerMockito.suppress(method); 複製代碼
綜上所述,在Android單元測試中,經過PowerMockito來隔離整個Android SDK以及項目業務的依賴,將單元測試的重心放在較細粒度(函數級別)的代碼邏輯,徹底可行。
@PrepareForTest
修飾的類單元測試覆蓋率變成0。這個問題暫時沒看到解決方案。