android自动化测试CTS源码分析之五

6. Instrumentation

6.1 启动Instrumentation

获取adb shell am instrument –w命令后,AM.java中的onRun方法对该命令处理如下

else if (op.equals("instrument")) {
            runInstrument();

runInstrument方法如下,

if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) {
            throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
        }

调用ActivityManagerService的startInstrumentation方法启动Instrumentation,并且传入相关命令参数。

final long origId = Binder.clearCallingIdentity();
            // Instrumentation can kill and relaunch even persistent processes
            forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
                    "start instr");
            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;
            Binder.restoreCallingIdentity(origId);

addAppLocked 方法中会调用startProcessLocked方法,到此和Activity等四大组件启动的流程完全一样。

最后在ActivityManagerService的attachApplicationLocked方法中,

Activity的启动主要调用ActivityStackSupervisor的attachApplicationLocked方法,

而Instrumentation的启动主要是调用ApplicationThreadNative的bindApplication方法,

然后跨进程调用ActivityThread的内部类ApplicationThread的bindApplication方法。

ProfilerInfo profilerInfo = profileFile == null ? null
                    : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
            thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());
            updateLruProcessLocked(app, false, null);
            app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();

当然,启动Instrumentation的过程中也会创建进程。

ActivityThread的内部类ApplicationThread的bindApplication方法部分代码如下,

AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableOpenGlTrace = enableOpenGlTrace;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            sendMessage(H.BIND_APPLICATION, data);

主要时发送handler消息切换到主线程执行。

Handler对BIND_APPLICATION消息处理如下,

AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);

handleBindApplication方法中有关instrumentation代码如下,

首先利用发射构造instrumentation对象,

try {
                java.lang.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);
            }

然后调用instrumentation对象的init方法。

mInstrumentation.init(this, instrContext, appContext,
                   new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
                   data.instrumentationUiAutomationConnection);

最后调用instrumentation对象的onCreate方法。

mInstrumentation.onCreate(data.instrumentationArgs);

6.2 Instrumentation作用

Instrumentation在自动化测试中主要作用是什么呢?

简单点说,进行自动化测试时,很多时候并不需要界面。因此可以这么说,在自动化测试中,Instrumentation作用相当于Activity。

其中的方法几乎和Activity对应的方法一摸一样。例如,

public void onCreate(Bundle arguments) {}
public void onStart() { }
public void onDestroy() { }

CTS的蓝牙apk的AndroidManifest.xml文件如下,

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.cts.bluetooth">

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />

    <application>
        <uses-library android:name="android.test.runner" />
    </application>

    <!-- This is a self-instrumenting test package. -->
    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                     android:targetPackage="com.android.cts.bluetooth"
                     android:label="CTS tests of bluetooth component">
        <meta-data android:name="listener"
            android:value="com.android.cts.runner.CtsTestRunListener" />
    </instrumentation>

</manifest>

也只是将instrumentation替换了activity.

AndroidJUnitRunner和InstrumentationTestRunner都是继承于Instrumentation,作用大同小异,

但是测试原理完全一样,因此本文选择InstrumentationTestRunner进行分析。

InstrumentationTestRunner扮演的角色类似于当中的UiAutomatorTestRunner类,

通过解析获取和建立目标测试用例和测试集然后开始测试。

onCreate方法调用流程图如下,


InstrumentationTestRunner的onStart方法如下,

try {
                 //新建打印结果对象
                StringResultPrinter resultPrinter = new StringResultPrinter(writer);
                  //设置测试监听
                mTestRunner.addTestListener(resultPrinter);
                   // 获取开始的系统时间
                long startTime = System.currentTimeMillis();
                  // 开始进行测试
                mTestRunner.runTest();
                   // 测试时间
                long runTime = System.currentTimeMillis() - startTime;
                   // 打印测试结果
                resultPrinter.printResult(mTestRunner.getTestResult(), runTime);
            } catch (Throwable t) {
                // catch all exceptions so a more verbose error message can be outputted
                writer.println(String.format("Test run aborted due to unexpected exception: %s",
                                t.getMessage()));
                t.printStackTrace(writer);
            } finally {
                mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
                        String.format("\nTest results for %s=%s",
                        mTestRunner.getTestClassName(),
                        byteArrayOutputStream.toString()));

                if (mCoverage) {
                    generateCoverageReport();
                }
                writer.close();
                   // 调用finish方法
                finish(Activity.RESULT_OK, mResults); 
            }
        }

6.3,测试

在InstrumentationTestRunner的onStart方法中会调用AndroidTestRunner的runTest进行测试, runTest方法如下,

public void runTest(TestResult testResult) {
        mTestResult = testResult; 
        for (TestListener testListener : mTestListeners) {
            mTestResult.addListener(testListener);
        }
        Context testContext = mInstrumentation == null ? mContext : mInstrumentation.getContext();
        for (TestCase testCase : mTestCases) {
            setContextIfAndroidTestCase(testCase, mContext, testContext);
            setInstrumentationIfInstrumentationTestCase(testCase, mInstrumentation);
            setPerformanceWriterIfPerformanceCollectorTestCase(testCase, mPerfWriter);
            testCase.run(mTestResult);
        }
    }

首先设置监听,然后逐个调用测试方法。

CTS测试中,每个apk测试的TestCase完全不一样,一般都继承于AndroidTestCase,而AndroidTestCase继承于TestCase。

以蓝牙测试为例,测试类BasicAdapterTest定义如下,

public class BasicAdapterTest extends AndroidTestCase {

该测试类有8个以test开头的测试用例。

具体的调用过程就不论述了,

TestCase的runBare方法如下,

public void runBare() throws Throwable {
		Throwable exception= null;
		setUp();
		try {
			runTest();
		} catch (Throwable running) {
			exception= running;
		}
		finally {
			try {
				tearDown();
			} catch (Throwable tearingDown) {
				if (exception == null) exception= tearingDown;
			}
		}
		if (exception != null) throw exception;
	}

1,在测试之前会调用setUp方法。

2,利用反射的方法进行测试用例的调用。

3,测试完成之后,会调用tearDown方法。

至于具体的测试用例,可以看蓝牙的测试。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值