instrumentation
先看activity的啟動(dòng)過程:
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
繼續(xù)看startActivityForResult:
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
這里我們看到activity的啟動(dòng)是由 instrumentation的execStartActivity執(zhí)行的笑窜。
這種設(shè)計(jì)即委托觀察者模式登澜。
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
自動(dòng)化測試,instrumentaiontestcase:
testactivity = (testClass)getInstrumentation().startActivitySync(intent);
text = testactivity.findViewById(R.id.text1);
assertEquals("hello,world", text.getText().toString());
...不知道源碼也可以的铐拐。
/** This testcase starts the same activity and sending a key event to that activity under test*/
private void testcase3() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(“STRING”, “Hai”);
intent.setClassName(getTargetContext(), "MyActivity.class");
MyActivity activity = (MyActivity)startActivitySync(_intent);
act = new ActivityRunnable(activity);
runOnMainSync(act);
舉個(gè)例子(from:http://stackoverflow.com/questions/1759626/how-do-you-test-an-android-application-across-multiple-activities)
package com.mycompany;
import android.app.*;
import android.content.*;
import android.test.*;
import android.test.suitebuilder.annotation.*;
import android.util.*;
import android.view.*;
import android.widget.*;
import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsNull.*;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.*;
import static com.mycompany.R.id.*;
public class LoginTests extends InstrumentationTestCase {
@MediumTest
public void testAValidUserCanLogIn() {
Instrumentation instrumentation = getInstrumentation();
// Register we are interested in the authentication activiry...
Instrumentation.ActivityMonitor monitor = instrumentation.addMonitor(AuthenticateActivity.class.getName(), null, false);
// Start the authentication activity as the first activity...
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(instrumentation.getTargetContext(), AuthenticateActivity.class.getName());
instrumentation.startActivitySync(intent);
// Wait for it to start...
Activity currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
assertThat(currentActivity, is(notNullValue()));
// Type into the username field...
View currentView = currentActivity.findViewById(username_field);
assertThat(currentView, is(notNullValue()));
assertThat(currentView, instanceOf(EditText.class));
TouchUtils.clickView(this, currentView);
instrumentation.sendStringSync("MyUsername");
// Type into the password field...
currentView = currentActivity.findViewById(password_field);
assertThat(currentView, is(notNullValue()));
assertThat(currentView, instanceOf(EditText.class));
TouchUtils.clickView(this, currentView);
instrumentation.sendStringSync("MyPassword");
// Register we are interested in the welcome activity...
// this has to be done before we do something that will send us to that
// activity...
instrumentation.removeMonitor(monitor);
monitor = instrumentation.addMonitor(WelcomeActivity.class.getName(), null, false);
// Click the login button...
currentView = currentActivity.findViewById(login_button;
assertThat(currentView, is(notNullValue()));
assertThat(currentView, instanceOf(Button.class));
TouchUtils.clickView(this, currentView);
// Wait for the welcome page to start...
currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
assertThat(currentActivity, is(notNullValue()));
// Make sure we are logged in...
currentView = currentActivity.findViewById(welcome_message);
assertThat(currentView, is(notNullValue()));
assertThat(currentView, instanceOf(TextView.class));
assertThat(((TextView)currentView).getText().toString(), is("Welcome, MyUsername!"));
}
}
uiautomator
簡單看一下uiautomator,首先吓著,instrumentation 用來做UI測試受制于android的安全策略箱蝠,本質(zhì)上是一個(gè)APP測試微峰,需要和被測APP在相同簽名下疲酌,相同的進(jìn)程下皇忿,才能互相信任,獲取控件狀態(tài)锄蹂。因此氓仲,如果不知道源碼,不要對(duì)APP重簽名得糜;另外敬扛,instrumentaion比較底層,封裝程度不高掀亩,因此不適合直接拿來測試舔哪。從android API LEVEL 16開始,android引入了新的測試框架uiautomator,它對(duì)UI測試做了高度封裝槽棍,同時(shí)不依賴于instrumentation,不需要做簽名校驗(yàn)捉蚤,可直接通過系統(tǒng)調(diào)用抬驴,獲取被測應(yīng)用控件。非常方便缆巧。以后詳細(xì)介紹布持。
來自微信公眾帳號(hào):myersguo
參考資料
android samples
instrumentationtestcase source code
instrumentation 源碼
Activity 源碼
uiautomatorTestCase源碼
uidevice 源碼
uiautomatorbridge源碼
automationg performance tests