1. 准备
- 首先安装被测试程序和包含测试case的程序
包含测试case的程序的AndroidManifest中包含有这么一行配置:
<instrumentation android:label="Tests for com.qiku.myapplication" android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.android.foo" android:handleProfiling="false" android:functionalTest="false" />
其中targetPackage
指定测试程序的包名
2.运行
adb shell am instrument -w com.android.foo.test/android.support.test.runner.AndroidJUnitRunner
会调用Am.java 中的runInstrument
方法
private void runInstrument() throws Exception {
....
ComponentName cn;
if (cnArg.contains("/")) {
cn = ComponentName.unflattenFromString(cnArg);
if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
}
InstrumentationWatcher watcher = null;
UiAutomationConnection connection = null;
if (wait) {
watcher = new InstrumentationWatcher();
watcher.setRawOutput(rawMode);
connection = new UiAutomationConnection();
}
if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
}
}
- cn是测试程序的component name
- 会创建InstrumentationWatcher用来监测instrumentation test运行的状态
- 会创建UiAutomationConnection用来接收UiAutomator的命令,运行一些shell进程才能进行的特权操作
- 最后会调用AMS的startInstrumentation
ActivityManagerService.java
public boolean startInstrumentation(ComponentName className,
String profileFile, int flags, Bundle arguments,
IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection,
int userId, String abiOverride) {
synchronized(this) {
InstrumentationInfo ii = null;
ApplicationInfo ai = null;
try {
ii = mContext.getPackageManager().getInstrumentationInfo(
className, STOCK_PM_FLAGS);
ai = AppGlobals.getPackageManager().getApplicationInfo(
ii.targetPackage, STOCK_PM_FLAGS, userId);
} catch (PackageManager.NameNotFoundException e) {
首先从className中找到InstrumentationInfo,得到target package的application info
ProcessRecord app = addAppLocked(ai, false, abiOverride);
app.instrumentationClass = className;
app.instrumentationInfo = ai;
app.instrumentationProfileFile = profileFile;
app.instrumentationArguments = arguments;
app.instrumentationWatcher = watcher;
app.instrumentationUiAutomationConnection = uiAutomationConnection;
app.instrumentationResultClass = className;
然后launch被测试的程序,将instrmentation的一些信息设置到ProcessRecord
中
在被测程序的application
创建后,调用AMS的attachApplicationLocked
时
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
}
会将app.instrumentationClass, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection传入。
在ActivityThread
的handleBindApplication
中
if (data.instrumentationName != null) {
try {
ii = new ApplicationPackageManager(null, getPackageManager())
.getInstrumentationInfo(data.instrumentationName, 0);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
"Unable to find instrumentation info for: " + data.instrumentationName);
}
mInstrumentationPackageName = ii.packageName;
mInstrumentationAppDir = ii.sourceDir;
mInstrumentationSplitAppDirs = ii.splitSourceDirs;
mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
mInstrumentedAppDir = data.info.getAppDir();
mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
mInstrumentedLibDir = data.info.getLibDir();
} else {
ii = null;
}
找出测试程序的InstrumentationInfo
// Continue loading instrumentation.
if (ii != null) {
final ApplicationInfo instrApp = new ApplicationInfo();
ii.copyTo(instrApp);
instrApp.initForUser(UserHandle.myUserId());
final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
try {
final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate instrumentation "
+ data.instrumentationName + ": " + e.toString(), e);
}
final ComponentName component = new ComponentName(ii.packageName, ii.name);
mInstrumentation.init(this, instrContext, appContext, component,
data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
if (mProfiler.profileFile != null && !ii.handleProfiling
&& mProfiler.profileFd == null) {
mProfiler.handlingProfiling = true;
final File file = new File(mProfiler.profileFile);
file.getParentFile().mkdirs();
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
}
} else {
mInstrumentation = new Instrumentation();
}
创建AppContext加载测试程序中的android.support.test.runner.AndroidJUnitRunner
AndroidJUnitRunner
继承自Instrumentation
,同时赋给mInstrumentation
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
mInstrumentation.callApplicationOnCreate(app);
调用Application的OnCreate之前,会调用AndroidJUnitRunner
的onCreate
AndroidJUnitRunner.java
@Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
setArguments(arguments);
specifyDexMakerCacheProperty();
start();
}
public void start() {
if (mRunner != null) {
throw new RuntimeException("Instrumentation already started");
}
mRunner = new InstrumentationThread("Instr: " + getClass().getName());
mRunner.start();
}
start()方法启动一个新的线程,然后调用AndroidJUnitRunner
的onStart
方法
@Override
public void onStart() {
super.onStart(); //等待UI 线程变成 idle
if (getBooleanArgument(ARGUMENT_DEBUG)) {
Debug.waitForDebugger();
}
setupDexmakerClassloader();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PrintStream writer = new PrintStream(byteArrayOutputStream);
List<RunListener> listeners = new ArrayList<RunListener>();
try {
JUnitCore testRunner = new JUnitCore();
addListeners(listeners, testRunner, writer);
TestRequest testRequest = buildRequest(getArguments(), writer);
Result result = testRunner.run(testRequest.getRequest());
result.getFailures().addAll(testRequest.getFailures());
} catch (Throwable t) {
// catch all exceptions so a more verbose error message can be displayed
writer.println(String.format(
"Test run aborted due to unexpected exception: %s",
t.getMessage()));
t.printStackTrace(writer);
} finally {
Bundle results = new Bundle();
reportRunEnded(listeners, writer, results);
writer.close();
results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
String.format("\n%s",
byteArrayOutputStream.toString()));
finish(Activity.RESULT_OK, results);
}
}
- 创建JUnitCore以运行Junit程序
- buildRequest创建测试请求
- 通过JUnitCore运行request
TestRequest buildRequest(Bundle arguments, PrintStream writer) {
// only load tests for current aka testContext
// Note that this represents a change from InstrumentationTestRunner where
// getTargetContext().getPackageCodePath() was also scanned
TestRequestBuilder builder = createTestRequestBuilder(writer,
getContext().getPackageCodePath());
...
return builder.build(this, arguments);
}
buildRequest
通过TestRequestBuilder
根据测试参数来创建TestRequest
,我们主要看如果没有参数的情况
TestRequestBuilder.java
public TestRequest build(Instrumentation instr, Bundle bundle) {
if (mTestLoader.isEmpty()) {
// no class restrictions have been specified. Load all classes
loadClassesFromClassPath();
}
Request request = classes(instr, bundle, mSkipExecution, new Computer(),
mTestLoader.getLoadedClasses().toArray(new Class[0]));
return new TestRequest(mTestLoader.getLoadFailures(), new LenientFilterRequest(request, mFilter));
}
从测试apk中找到测试类
private void loadClassesFromClassPath() {
Collection<String> classNames = getClassNamesFromClassPath();
for (String className : classNames) {
mTestLoader.loadIfTest(className);
}
}
网友评论