安卓測(cè)試主要包括兩種:?jiǎn)卧獪y(cè)試;集成測(cè)試;測(cè)試主要通過(guò)mock依賴,驗(yàn)證行為。 Mock框架、單元測(cè)試框架、集成測(cè)試框架是TDD所需的主要工具。
times(int),never(),atLeastOnce(),atLeast(int),
atMost(int)驗(yàn)證調(diào)用次數(shù),verify默認(rèn)的是times(1),因此無(wú)需顯式指定doThrow(new RuntimeException()).when(mockedList).clear();InOrder可以驗(yàn)證調(diào)用的順序(不同語(yǔ)句),原則上只需要驗(yàn)證關(guān)鍵邏輯,沒必要
所有調(diào)用都需要驗(yàn)證、甚至其順序only()的語(yǔ)義:僅有該方法被調(diào)用,且僅被調(diào)用一次never()的語(yǔ)義:該方法未被調(diào)用過(guò)verifyZeroInteractions(...)的語(yǔ)義:mock對(duì)象沒有任何方法調(diào)用verifyNoMoreInteractions(...):驗(yàn)證沒有其他的調(diào)用(除了此前驗(yàn)證的方法)與其他框架/工具的整合
RxJava
myObservable.toBlocking().first();進(jìn)行同步TestSubscriber,該類提供了眾多實(shí)用的方法,例如同步獲取
Observable發(fā)射的所有對(duì)象,斷言沒有onError,等待onComplete等處理生產(chǎn)代碼中的異步問(wèn)題,可以在測(cè)試時(shí)hook Schedulers.io()為
Schedulers.immediate(),但在hook之前需要reset RxJavaPlugins,
在RxJava中這個(gè)方法是package private,所以做到這點(diǎn)有點(diǎn)hack,在RxAndroid中則
無(wú)需如此麻煩,因?yàn)榛蛘逺xAndroid提供了很好的hook支持。
package rx.plugins;
public class RxJavaTestPlugins extends RxJavaPlugins {
RxJavaTestPlugins() {
super();
}
public static void resetPlugins(){
getInstance().reset();
}
}
...
RxJavaTestPlugins.resetPlugins();
RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() {
@Override
public Scheduler getIOScheduler() {
return Schedulers.immediate();
}
});
另外有一點(diǎn)需要指出的是,要想RxJava的hook起作用,必須要在Schedulers
類初始化之前進(jìn)行hook,那么在測(cè)試的時(shí)候,只能通過(guò)實(shí)現(xiàn)自定義的TestRunner來(lái)做到了
,在TestRunner的構(gòu)造函數(shù)中進(jìn)行reset和hook就OK了。而RxAndroid的hook則沒這
么麻煩,在測(cè)例的setUp函數(shù)中進(jìn)行就OK。
ObservableExecutionHook,示例:
RxJavaPlugins.getInstance().registerObservableExecutionHook(new DebugHook(new DebugNotificationListener() {...}測(cè)試框架
Espresso
同步問(wèn)題:自動(dòng)處理UI Event/AsyncTask。當(dāng)使用Retrofit時(shí),可以為 測(cè)試代碼生成測(cè)試用的RestAdapter,指定Excutor為AsyncTask:
new RestAdapter.Builder().setExecutors(AsyncTask.THREAD_POOL_EXECUTOR,
new MainThreadExecutor())
.build();
測(cè)試Save and restore state,觸發(fā)代碼:
private void rotateScreen() {
Context context = InstrumentationRegistry.getTargetContext();
int orientation
= context.getResources().getConfiguration().orientation;
Activity activity = activityRule.getActivity();
activity.setRequestedOrientation(
(orientation == Configuration.ORIENTATION_PORTRAIT) ?
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE :
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
Retrofit