單元測試業界標準:
MVP + Junit4 + Mockito + Hamcrest + Espresso + Dragger2
一、爲何要做單元測試
- App持續集成的時候需要一個集成測試保障其正確性(正確性)
- 頁面較複雜的時候,我們是否可以先測試業務邏輯的正確性(邊開發邊測試)
- 項目較大,編譯緩慢,測試業務邏輯部分(測試速度大幅提高)
- 對代碼結構、代碼健壯性、代碼可維護度都有很大提高
不用太擔心修改代碼後,會導致其它bug,單元測試集成測試會告訴我們
other:蘑菇街的哥們說過:慢慢的你會發現,其實不是這樣的,純java的代碼其實真不少,而且往往是核心的邏輯所在
二、測些什麼
代碼結構:
Android中的MVC分層不清晰,我們更傾向於MVP,這裏列舉一些比較好的文章。
測試框架:
Android單元測試分爲:
Local Unit Tests (直接在JVM上進行測試):
1.Junit4
2.Mockito (最常用的Mock之一)
3.Hamcrest(增加JUnit的測試能力)
4.Robolectric(第三方:JVM中模擬Android)
Instrumented Tests (需要藉助儀器、模擬器測試):
1.AndroidJUnitRunner (官方:InstrumentTestRunner升級版,支持Junit4,下面的UI測試框架需要與之結合使用)
2.Espresso (官方:測試單獨App)
3.UI Automator (官方:跨進程測試(多App之間進行測試))
4.Robotium(第三方:基於Android 原生的Instruments)
5.Appium (第三方:支持Android IOS自動化測試)
- OS: Apple’s UIAutomation
- Android 4.2+: Google’s UiAutomator
- Android 2.3+: Google’s Instrumentation. (Instrumentation support is provided by bundling a separate project, Selendroid)
測些什麼
- Local Tests 測試業務邏輯層、部分Model層
- Instrumented Tests 測試UI,測試用例
- 測試方法的返回值
- 測試操作流程中方法的調用情況(是否調用?調用幾次?被調用時傳入的參數值)
三、測試準備
1. Junit4
- Assert.assertXXX :斷言
- 參數化測試
- @Suite.SuiteClasses
- JUnit Rules之Github
- JUnit Rules源碼分析
2. Mockito
Mock的概念(Mockito.mock):所謂的mock就是創建一個類的虛假的對象,在測試環境中,用來替換掉真實的對象,以達到兩大目的:
- 驗證這個對象的某些方法的調用情況,調用了多少次,參數是什麼等等
- 指定這個對象的某些方法的行爲,返回特定的值,或者是執行特定的動作
- 注意:需要將Mock對象注入到需要測試的類中
@RunWith(MockitoJUnitRunner.class)
@Mock > (Mock出一個對象,此對象的方法調用並不會真正調用)
@Spy > (Spy出的對象,調用其方法會真正執行)
@Captor > (用於抓取被調用方法的參數,進行驗證,也可以使用ArgumentCaptor new出來)
@InjectMocks > (只要在被測試類上標記@InjectMocks,Mockito就會自動將標記@Mock、@Spy等註解的屬性值注入到被測試類中。)
// 抓取mock對象方法的參數進行驗證:
// 驗證mTasksRepository對象調用了getTask,並且抓取此方法的第二個參數
verify(mTasksRepository).getTask(eq(testTask.getId()), mGetTaskCallbackCaptor.capture());
// 調用上一步抓取到的參數的onTaskLoaded方法
mGetTaskCallbackCaptor.getValue().onTaskLoaded(testTask);
// 預設mock對象某方法被調用時返回特定值:
Mockito.when(searchSchoolFragmentView.isActive()).thenReturn(true);
// 預設mock對象某方法被調用時執行doAnswer裏面自定義的邏輯:
// 也可以先
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
//這裏可以獲得傳給performLogin的參數
Object[] arguments = invocation.getArguments();
//callback是第三個參數
NetworkCallback callback = (NetworkCallback) arguments[2];
callback.onFailure(500, "Server error");
return 500;
}
}).when(mockUserManager).performLogin(anyString(), anyString(), any(NetworkCallback.class));
3. Hamcrest
Assert.assertThat(object,Matcher.xxxmethod(yy));
Matchers:匹配器 > Espresso也基於此
4. Espresso
組成部分:
- ViewMachers:尋找用來測試的View
- ViewAction:發送交互事件
- ViewAssertions:檢測測試結果
View Machers: withId、withHint、allof(withId(),withHint())…
View Actions: click()、doubleClick()、pressBack()、openLick()…
View Assertions: matches、not、isDisplayed…
線程安全保障:
Espresso測試有個很強大的地方是它在多個測試操作中是線程安全的。
Espresso會等待當前進程的消息隊列中的UI事件,並且在任何一個測試操作中會等待其中的AsyncTask結束纔會執行下一個測試。這能夠解決程序中大部分的線程同步問題。
Espresso中有個API叫做registerIdlingResource,它可以讓你使用自定義的線程安全邏輯。(上面鏈接中有對registerIdlingResource詳細講解)
四、參考資料
蘑菇街小創
http://chriszou.com/
Android單元測試
http://www.chriszou.com/2016/04/13/android-unit-testing-start-from-what.html
解讀Android官方MVP項目單元測試
http://www.jianshu.com/p/cf446be43ae8
Android官網測試文檔
http://developer.android.com/intl/zh-tw/training/testing/start/index.html
dragger2:
(一)http://www.jianshu.com/p/cd2c1c9f68d4
(二)http://www.jianshu.com/p/1d42d2e6f4a5
(三)http://www.jianshu.com/p/65737ac39c44
dragger2:
http://www.cnblogs.com/tiantianbyconan/p/5092525.html
Android谷歌官方測試demo:
https://github.com/googlesamples/android-testing
Mockito講解
http://www.vogella.com/tutorials/Mockito/article.html
Mockito更詳細講解
http://hotdog.iteye.com/blog/search?query=Mockito
Espresso
http://blog.csdn.net/shandong_chu/article/details/47083753
Espresso IdlingResource
http://www.jianshu.com/p/95d075b90a2f
MVP快速開發框架Beam
https://github.com/Jude95/Beam
encleus簡化MVP
https://github.com/konmik/nucleus