美文网首页AndroidTest
Android 运行Instrumentation Test的原

Android 运行Instrumentation Test的原

作者: yangweigbh | 来源:发表于2017-10-23 21:44 被阅读679次

1. 准备

  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传入。

ActivityThreadhandleBindApplication

        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之前,会调用AndroidJUnitRunneronCreate

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()方法启动一个新的线程,然后调用AndroidJUnitRunneronStart方法

    @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);
        }
    }

相关文章

网友评论

    本文标题:Android 运行Instrumentation Test的原

    本文链接:https://www.haomeiwen.com/subject/yffzuxtx.html