背景
- 自動化測試開發過程中,由于對注解執行順序不了解,可能發生時序異常,也會造成代碼質量低
- 各種注解所運行的線程不了解
目的
- 掌握各種注解的執行時機
- 提高代碼開發質量
執行順序圖
image
收益
- 發現度假在@Before中進行了mock,mock不生效
- 打開了思維,對每個注解有了了解,可以在不同的注解中進行不同的事務
源碼分析
public T launchActivity(@Nullable Intent startIntent) {
...
if (null == startIntent) {
startIntent = getActivityIntent();
if (null == startIntent) {
...
startIntent = new Intent(Intent.ACTION_MAIN);
}
}
...
beforeActivityLaunched(); //子線程
mActivity = mActivityClass.cast(mInstrumentation.startActivitySync(startIntent));//異步啟動activity
mInstrumentation.waitForIdleSync(); //子線程阻塞
if (mActivity != null) {
afterActivityLaunched(); //子線程
} else {
...
}
return mActivity;
}
public void waitForIdleSync() {
validateNotAppThread(); //判斷不是UI線程
Idler idler = new Idler(null);
mMessageQueue.addIdleHandler(idler); //添加IdlerHandler
mThread.getHandler().post(new EmptyRunnable());
idler.waitForIdle(); //阻塞
}
private static final class Idler implements MessageQueue.IdleHandler {
private final Runnable mCallback;
private boolean mIdle;
public Idler(Runnable callback) {
mCallback = callback;
mIdle = false;
}
public final boolean queueIdle() {
if (mCallback != null) {
mCallback.run();
}
synchronized (this) {
mIdle = true;
notifyAll(); //解除阻塞
}
return false;
}
public void waitForIdle() {
synchronized (this) {
while (!mIdle) {
try {
wait(); //阻塞
} catch (InterruptedException e) {
}
}
}
}
}
源碼相關類
- AndroidJUnitRunner 自動化測試入口 Runner
- TestRequestBuilder 測試前請求構建類helper
- LenientFilterRequest Request包裝類
- AndroidJUnit4ClassRunner test方法的Runner類 ,繼承ParentRunner
- ExtendedSuite(Runner)class的Runner類 繼承ParentRunner ;保存著AndroidJUnit4ClassRunner(test方法Runner)
- TestExecutor 測試執行類 運行ExtendedSuite中的run()開始執行Statement,啟動入口;
- Statement 執行各種注解對應的方法執行順序
- ExtendedSuite中的方法執行順序為:
RunRules(@ClassRule) ->RunAfters(@AfterClass) ->RunBefores(@BeforeClass); - AndroidJUnit4ClassRunner中的方法執行順序為:
RunRules(@Rule MethodRule和TestRule) ->RunAfters(@After) ->RunBefores(@Before) ->FailOnTimeout(@Test) ->ExpectException(@Test) ->InvokeMethod(@Test) - 注解執行順序
ExtendedSuite @ClassRule-> @BeforeClass -> {AndroidJUnit4ClassRunner} -> @AfterClass
AndroidJUnit4ClassRunner @Rule-> @Before-> @Test-> @After
- ExtendedSuite中的方法執行順序為:
- Statement鏈式調用
//ParentRunner
protected Statement classBlock(final RunNotifier notifier) {
Statement statement = childrenInvoker(notifier);
if (!areAllChildrenIgnored()) {
statement = withBeforeClasses(statement);
statement = withAfterClasses(statement);
statement = withClassRules(statement);
}
return statement;
}
//BlockJUnit4ClassRunner
protected Statement methodBlock(FrameworkMethod method) {
Object test;
try {
test = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest();
}
}.run();
} catch (Throwable e) {
return new Fail(e);
}
Statement statement = methodInvoker(method, test);
statement = possiblyExpectingExceptions(method, test, statement);
statement = withPotentialTimeout(method, test, statement);
statement = withBefores(method, test, statement); //@before
statement = withAfters(method, test, statement); 參數statement對應@Before
statement = withRules(method, test, statement);
return statement;
}
//@After
public class RunAfters extends Statement {
private final Statement next;
private final Object target;
private final List<FrameworkMethod> afters;
public RunAfters(Statement next, List<FrameworkMethod> afters, Object target) {
this.next = next;
this.afters = afters;
this.target = target;
}
@Override
public void evaluate() throws Throwable {
List<Throwable> errors = new ArrayList<Throwable>();
try {
next.evaluate(); //這個statement對應@Before
} catch (Throwable e) {
errors.add(e);
} finally {
for (FrameworkMethod each : afters) {
try {
each.invokeExplosively(target);
} catch (Throwable e) {
errors.add(e);
}
}
}
MultipleFailureException.assertEmpty(errors);
}
}