UiAutomator UIAutomator2.0 简介

恒温 · 2015年11月08日 · 最后由 Forwards 回复于 2018年09月04日 · 10054 次阅读
本帖已被设为精华帖!

UIAutomator2.0 出来有一段时间了。

我们就看一句话

Most importantly, UIAutomator is now based on Android Instrumentation

Instrumentation 终于把 UIAutomator 纳入了他的麾下。

UIAutomator2.0 出来之后,投入使用的似乎不多,官方的文档也不多,而且还有错。😢 不过各个测试框架也在积极拥抱变化,比如 Appium,Selendroid。最快的极客学院也推出了 UIAutomator2.0 的教程。当然我们 TesterHome 也有同学上了一把,比如在 Android studio 上运用 UI Automator 执行自动化测试。新东西出来了,不代表旧事物就不能用了。之前的 UIAutomator 一样可以跑的很欢,守旧的同学可以继续。

事实上,Google 提供的 Android Testing Support Library 变化的挺多的,比如全面拥抱 Junit 4.0,力荐使用 AndroidJUnitRunner,然后使用 Android Studio IDE 等等。反正不仅在开发端,在测试端,Google 也在做一些标准和统一,然后让我们忘记之前的 monkeyrunner, instruments,只剩下:

  • AndroidJUnitRunner: JUnit 4-compatible test runner for Android
  • Espresso: UI testing framework; suitable for functional UI testing within an app
  • UI Automator: UI testing framework; suitable for cross-app functional UI testing across system and installed apps

废话时间结束。

如何写一个 UIAutomator2.0 的工程?

在这之前,我是这样写 UIAutomator 的, 参见如何使用和调试 android UIAutomator ?。我记性不好,就算现在写,也会翻翻自己的文章。我试着也使用 maven 来配置,不过失败告终。因为在 maven 库里面,找不到 com.android.support.test.*,因为貌似所有的测试依赖都移到这里来了。而在你电脑上,它藏在 android-sdk/extras/android/m2repository 下面,感觉上它也可以成为你 local maven 库中的一个,不过没时间研究。关于安装 Android Maven 本地依赖库可以帮你开拓下思维,但是然并软。所以老老实实使用 gradle 吧。

更新下,如果不想用 gradle,想直接使用 jar 包做依赖的话,可以下载 https://github.com/googlesamples/android-testing/tree/master/ui/espresso/BasicSampleBundled/libs 下面的 jar。

创建一个 android 工程

对于 Instrumentation 大家应该都熟知,如何写一个 Instrumentation 工程,百度一搜一大把,大家模仿网上教程的时候用点心,别抄成个二百五。UIAutomator2.0 是基于 Instrumentation,所以基本上,和 Instrumentation 一样,它也有一个测试应用的概念。我们打开 AS,创建一个空应用,比如我的:

提示:Gradle 加载可能有点慢,自备 *** 会来的快一点。

添加依赖和测试代码

  1. 先在 build.grade 里面添加依赖。

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 23
        buildToolsVersion "23.0.1"
    
        defaultConfig {
            applicationId "lihuazhang.testerhome.com.uiautomator2"
            minSdkVersion 19
            targetSdkVersion 23
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" # 这是我添加的
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:23.1.0'
    
        # 这以下是我添加的
        androidTestCompile 'com.android.support:support-annotations:23.1.0'
        androidTestCompile 'com.android.support.test:runner:0.4'
        // Set this dependency to use JUnit 4 rules
        androidTestCompile 'com.android.support.test:rules:0.4'
        // Set this dependency to build and run Espresso tests
        androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
        // Set this dependency to build and run UI Automator tests
        androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
    
    }
    
    
  2. 在 androidTest 下面添加测试代码

    package lihuazhang.testerhome.com.uiautomator2;
    
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.content.pm.ResolveInfo;
    import android.support.test.InstrumentationRegistry;
    import android.support.test.filters.SdkSuppress;
    import android.support.test.runner.AndroidJUnit4;
    import android.support.test.uiautomator.By;
    import android.support.test.uiautomator.UiDevice;
    import android.support.test.uiautomator.UiObject2;
    import android.support.test.uiautomator.Until;
    
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    
    import static org.hamcrest.core.IsNull.notNullValue;
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertThat;
    
    /**
     * Created by lihuazhang on 15/11/8.
     */
    
    @RunWith(AndroidJUnit4.class)
    @SdkSuppress(minSdkVersion = 18)
    public class CalculatorTest {
        private UiDevice mDevice;
        private static final int LAUNCH_TIMEOUT = 5000;
        private final String BASIC_SAMPLE_PACKAGE = "com.android.calculator2";
    
        @Before
        public void setUp() {
            // Initialize UiDevice instance
            mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    
            // Start from the home screen
            mDevice.pressHome();
    
            // Wait for launcher
            final String launcherPackage = getLauncherPackageName();
            assertThat(launcherPackage, notNullValue());
            mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT);
        }
        @Test
        public void checkPreconditions() {
            assertThat(mDevice, notNullValue());
        }
    
        @Test
        public void calculatorTest() {
            mDevice.findObject(By.desc("应用")).click();
            mDevice.wait(Until.hasObject(By.desc("计算器")), LAUNCH_TIMEOUT);
            mDevice.findObject(By.desc("计算器")).click();
    
            UiObject2 button7 = mDevice.wait(Until.findObject(By.res("com.android.calculator2", "digit_7")), 500);
            UiObject2 buttonX = mDevice.wait(Until.findObject(By.res("com.android.calculator2", "op_mul")), 500);
            UiObject2 button6 = mDevice.wait(Until.findObject(By.res("com.android.calculator2", "digit_6")), 500);
            UiObject2 buttonEqual = mDevice.wait(Until.findObject(By.res("com.android.calculator2", "eq")), 500);
            UiObject2 output = mDevice.wait(Until.findObject(By.res("com.android.calculator2", "result")), 500);
    
            button7.click();
            buttonX.click();
            button6.click();
            buttonEqual.click();
            assertEquals(output.getText(), "42");
    
        }
    
        private String getLauncherPackageName() {
            // Create launcher Intent
            final Intent intent = new Intent(Intent.ACTION_MAIN);
            intent.addCategory(Intent.CATEGORY_HOME);
    
            // Use PackageManager to get the launcher package name
            PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
            ResolveInfo resolveInfo = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
            return resolveInfo.activityInfo.packageName;
        }
    }
    
    

运行

运行很简单,右击测试文件,点击运行即可。

如果在命令行的话,直接运行 ./gradlew cC

这个时候,我们打开 adb shell

那既然是 instrumentation,其实我们可以使用这样的命令来执行。

➜  connected  adb shell am instrument -w lihuazhang.testerhome.com.uiautomator2.test/android.support.test.runner.AndroidJUnitRunner

lihuazhang.testerhome.com.uiautomator2.CalculatorTest:..

Time: 7.014

OK (2 tests)

我没找到 uiautomator 1.0 的 nohup 形式,不知道其他人知道不。

报告

感谢 @carl 的提醒,漂亮的报告还是需要的。./gradlew cC 生成的报告在 Uiautomator2/app/build/reports/androidTests/connected 位置。

注意点

  1. 不要继承 InstrumentationTestCase

    This class is deprecated. It is no longer necessary to extend UiAutomatorTestCase. You can use getInstance(Instrumentation) from any test class as long as you have access to an Instrumentation instance.

    既然 UiAutomatorTestCase 都不用继承了,更别提 InstrumentationTestCase 了。官方文档里部分代码还在继承 InstrumentationTestCase,当然不会引起啥错误,但是在命令行使用 gradle cC 的时候,测试用例不会被执行。
    更新下,@carl 说他是继承了 InstrumentationTestCase 的,所以我又试验了下。gradle cC 运行的时候报错:

  2. mDevice.wait(Until.findObject(By.res("com.android.calculator2", "digit_7")), 500); 必要的等待还是需要的。众所周知,UI 自动化,如果不加入必要的等待策略,那么很难保证测试用例的鲁棒性。

  3. 我在执行测试用例的时候,使用的 Genymotion 模拟器,但是发现了一些问题,比如这个计算器的用例在 Genymotion 中无法通过。

  4. 遇到此类异常 UiAutomationService android.accessibilityservice.IAccessibilityServiceClient$Stub$Proxy@52c58464already registered!,先看看设备上是否有 uiautomator 在运行,有的话杀之。

  5. 在代码里的 System.out.println 是会输出在 logcat 的日志里。此外执行信息也会出现在 logcat 中。

参考文档

  1. UIAutomator2.0 的 API 文档
  2. Testing Support Library
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 54 条回复 时间 点赞

据我实际经验,我是继承了 InstrumentationTestCase 的,gradle cC 是可以执行的,它是全部执行。
非继承的方式在极客学院上就是这样用的,看过了,不错的。
本文没有对执行结果描述一下,有点尴尬,毕竟结果很重要,2.0 会生成一份网页的测试报告的,比 1.0 好很多,如果能修改生成报告的部分,根据自己的需要客制化一下就好了。

@carl 建议你再运行一次,然后看看你的 test runner 是哪个?
com.android.builder.testing.ConnectedDevice > No tests found.[Nexus 5 - 6.0] FAILED
No tests found. This usually means that your test classes are not in the form that your test runner expects (e.g. don't inherit from TestCase or lack @Test annotations).
:app:connectedDebugAndroidTest FAILED

FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ':app:connectedDebugAndroidTest'.

    There were failing tests. See the report at: file:///Users/lihuazhang/code/Uiautomator2/app/build/reports/androidTests/connected/index.html

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

技能 GET 起来~~

uiautomator2.0 让执行测试更快, 更强.是个很强大的改进. 这对 appium 是重大的利好.
不过传统插桩的方式仍然非常重要, 还是离不开. 兼容性测试是绕不过去的

哈哈,我宿舍电脑硬盘前几天坏了没环境,明天把公司的测试用例带回来反驳下不可以继承的观点。其实目前我最想知道的是测试报告,这个数据是怎么来的,是不是在手机某个目录下有的,又导出到了 PC 端,毕竟用例是在手机上跑的。它的原理是什么?有没有方法修改,比如说在原来的报告中加入更多的信息。

#5 楼 @carl 可以研究下。

#5 楼 @carl 测试报告是 android gradle 插件使用 ddmlibs 调用 adb shell,然后分析 adb shell 的 instrumentation log, 格式化成数据后输出成 html。 可以参考 android gradle 插件源码

#7 楼 @xiaoyy 感谢,我先去了解下,有问题再请教。

#7 楼 @xiaoyy 这段代码在哪里啊?

恒温 #53 · 2015年11月12日 Author

首先我们得知道,我们的报告生成的数据是在 app/build/outputs/androidTest-results/connected/xxx.xml 这个文件里。

文件名的命名:com.android.builder.internal.testing.CustomTestRunListener.java

@Override
protected File getResultFile(File reportDir) throws IOException {
    return new File(reportDir,
            "TEST-" + mDeviceName + "-" + mProjectName + "-" + mFlavorName + ".xml");
}

然后 CustomTestRunListener 继承于 com.android.ddmlib.testrunner.XmlTestRunListener,于是就能追到 https://android.googlesource.com/platform/tools/base/+/master/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java 里面去了。

瞧瞧看这个方法:


/**
  * Creates a report file and populates it with the report data from the completed tests.
  */
 private void generateDocument(File reportDir, long elapsedTime) {
     String timestamp = getTimestamp();
     OutputStream stream = null;
     try {
         stream = createOutputResultStream(reportDir);
         KXmlSerializer serializer = new KXmlSerializer();
         serializer.setOutput(stream, SdkConstants.UTF_8);
         serializer.startDocument(SdkConstants.UTF_8, null);
         serializer.setFeature(
                 "http://xmlpull.org/v1/doc/features.html#indent-output", true);
         // TODO: insert build info
         printTestResults(serializer, timestamp, elapsedTime);
         serializer.endDocument();
         String msg = String.format("XML test result file generated at %s. %s" ,
                 getAbsoluteReportPath(), mRunResult.getTextSummary());
         Log.logAndDisplay(LogLevel.INFO, LOG_TAG, msg);
     } catch (IOException e) {
         Log.e(LOG_TAG, "Failed to generate report data");
         // TODO: consider throwing exception
     } finally {
         if (stream != null) {
             try {
                 stream.close();
             } catch (IOException ignored) {
             }
         }
     }
 }

就是说每次执行都会把结果记录在 mRunResult 里,到结束后,写入到结果中去。

@carl

#10 楼 @lihuazhang 插件是在类 CustomTestRunListener 先生成 xml, 然后 TestReport 类转换为可视化的 html 文档. 前两天没上 testerhome 没有回复 不好意思

恒温 #51 · 2015年11月13日 Author

#11 楼 @xiaoyy 我已经找到了。多谢。

#12 楼 @lihuazhang 这个是不是需要源码?

恒温 #14 · 2015年11月13日 Author

#13 楼 @babyshine 。。。你看完帖子了吗?

#14 楼 @lihuazhang 跟我之前做的不太一样,我那个是打成 jar 包,然后就只写 case,没有 app 源码这部分

恒温 #16 · 2015年11月13日 Author

#15 楼 @babyshine 我测试的是计算器,你看到我用计算器的源码了吗?

#16 楼 @lihuazhang 这倒是没有,不错不错

#17 楼 @babyshine 我给你们之后这回复跪了。。。

#18 楼 @monkey 两年前用 uiautomator 了,还以为升级了

#16 楼 @lihuazhang @babyshine 虽然不用计算器的源码. 但是 instrumenration 倒是需要一个宿主工程. 和 uiautomator 不一样的地方

旧的 uiautomator 版本 getcurrentActivity() 返回不正确,2.0 修复这个问题了吗?
或者有其他的方式能得到当前页面的 activity name 吗?

求教 eclipse 的教程,执行一直失败
[2015-12-02 10:41:44 - lijingwu] ------------------------------
[2015-12-02 10:41:44 - lijingwu] Android Launch!
[2015-12-02 10:41:44 - lijingwu] adb is running normally.
[2015-12-02 10:41:44 - lijingwu] Performing android.test.InstrumentationTestRunner JUnit launch
[2015-12-02 10:41:44 - lijingwu] Automatic Target Mode: using device 'd84e272b'
[2015-12-02 10:41:44 - lijingwu] WARNING: Application does not specify an API level requirement!
[2015-12-02 10:41:44 - lijingwu] Device API version is 19 (Android 4.4.4)
[2015-12-02 10:41:44 - lijingwu] Uploading lijingwu.apk onto device 'd84e272b'
[2015-12-02 10:41:45 - lijingwu] Installing lijingwu.apk...
[2015-12-02 10:41:54 - lijingwu] Success!
[2015-12-02 10:41:54 - lijingwu] Launching instrumentation android.test.InstrumentationTestRunner on d84e272b
[2015-12-02 10:41:54 - lijingwu] Failed to launch test

恒温 #40 · 2015年12月02日 Author

#22 楼 @lijingwu eclipse 没用过啊。 你能说说你具体怎么部署的嘛?

#23 楼 @lihuazhang

这是我的工程文件,代码是 copy 你的教程里面的,

这是.xml 文件内容

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android_test_demo.test.test"
    android:versionCode="32"
    android:versionName="32.0" >

    <instrumentation
        android:name="android.test.InstrumentationTestRunner"
        android:targetPackage="com.example.android_test_demo.test.test" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <uses-library android:name="android.test.runner" />
    </application>

</manifest>
25楼 已删除
26楼 已删除
恒温 #27 · 2015年12月03日 Author

#24 楼 @lijingwu runner 不对

使用 uiautomator+gradle 生成的报告比较简洁,有没有方法加入一些日志信息到报告中!求解!非常感谢

这个是不是不支持 4.2 的手机啊

#11 楼 @xiaoyy 请教一个问题:怎么向增加一些用户自定义信息?我使用 log.i() / log.e() system.out.println() 来输入用户自定义信息,为什么都没有从 html 中找到?

#28 楼 @ouguangqian 这个问题找到解决方法了么?我自己试着用 log.i() 和 system.out.println() 都不行

请教:UiAutomatorTestCase 不继承后,如何实现 teardown?现在每条 case 跑完后,直接停留在最后的页面,没有关闭应用。这对 case 的独立性是不利的。谢谢!

恒温 #30 · 2016年01月06日 Author

#32 楼 @kx5156 自己不能写 teardown 吗?

#33 楼 @lihuazhang 把 InstrumentationTestCase 中的 tearDown 的实现 copy 过来,不起作用;通过 java 执行 “adb shell am force-stop xxx” 也关不掉。

@lihuazhang 请教一个问题,是用 uiautomatorviewer 查看元素的时候,应该可以看见 webview 的内容,但是我这却看不到,能帮忙解决一下不?

#10 楼 @lihuazhang 你用的 runner 是哪个?为什么我没有找到对 CustomTestRunListener 的添加呢?

各位大神,为什么我通过 UiObject2 obj = mDevice.findObject(By.text("应用")); 找出来的对象是 null 啊,我的虚拟机也是 geny。而且也到了桌面 home 了。 在@Test的方法第一行运行这代码是 null

25楼 已删除

学习 uiautomator2.0 有一段时间了,目前想真正测一下 APP,但是遇到一个问题:怎么在 android Studio 里部署整个项目呢,比如方法的封装,控件的定位,用例等等,自己没有一个清晰的思路,哪位大神可以把自己 uiautomator2 的测试框架告知一下,以备借鉴,十分感谢,小弟初学,比较菜,请多指教

恒温 #40 · 2016年06月18日 Author

#39 楼 @xxb 你是想学 android app 开发吧。。

#40 楼 @lihuazhang 不是 我就是想学习一下整个测试框架的部署,目前没有一个清晰的思路,不知道大家用 uiautomator2 的时候整个测试项目是如何部署的

我如果要知道"開啟 app"要花費多少時間 (response time),是不是可以用 mDevice.wait() 去實現?

@Test
public void openApp(){
        long startTime = System.nanoTime();

        //open the app
        Context context = InstrumentationRegistry.getContext();
        final Intent intent = context.getPackageManager()
                .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
         //Clear out any previous instances
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        context.startActivity(intent);

       // Wait for the app to appear
       mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT);

      long endTime = System.nanoTime();

     Log.w(LOG_TAG, (endTime - startTime));
}

Logcat 顯示出的時間差就會是我要的 response time? 點擊按鈕切換介面也可以這樣做嗎?
另外我看了文件還不是很理解為什麼要有 “LAUNCH_TIMEOUT”

大神们,亲爱的大神们,2.0 支持 Context 吗,可以再 2.0 的自动化代码中实现发送广播不~不胜感谢~

#43 楼 @testblue 完全可以啊,拿到 Context 啊。 getInstrumentation().getContext 就 可以了。。
但是 发广播 用辅助的 APK 弹通知,实在是没有必要

#44 楼 @gybin02 是这样啊,因为我需要在完成一个自动话后告诉另一个服务它已经完成,那就需要给这个服务发送一个广播告诉它,是这么个意思~

#1 楼 @carl 你好! 我这里执行命令行(terminal)gradle Cc 时 编译出错,如下(直接在 AS 运行测试工程正常):怎么解决,麻烦了
FAILURE: Build failed with an exception.

  • What went wrong:
    A problem occurred configuring project ':app'.

    org.gradle.api.internal.tasks.DefaultTaskInputs$TaskInputUnionFileCollection cannot be cast to org.gradle.api.internal.file.collections.DefaultConfigur
    ableFileCollection

  • Try:
    Run with --info or --debug option to get more log output.

  • Exception is:
    org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':app'.
    at org.gradle.configuration.project.LifecycleProjectEvaluator.addConfigurationFailure(LifecycleProjectEvaluator.java:79)
    at org.gradle.configuration.project.LifecycleProjectEvaluator.notifyAfterEvaluate(LifecycleProjectEvaluator.java:74)
    at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:61)
    at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:573)
    at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:125)
    at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:47)
    at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:38)
    at org.gradle.initialization.DefaultGradleLauncher$2.run(DefaultGradleLauncher.java:124)
    at org.gradle.internal.Factories$1.create(Factories.java:22)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:53)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:121)
    at org.gradle.initialization.DefaultGradleLauncher.access$200(DefaultGradleLauncher.java:32)
    at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:98)
    at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:92)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:63)
    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:92)
    at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:83)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:94)
    at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:43)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:28)
    at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:82)
    at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:49)
    at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:59)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:49)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
    at org.gradle.util.Swapper.swap(Swapper.java:38)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:60)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:72)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.HintGCAfterBuild.execute(HintGCAfterBuild.java:44)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
    at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:240)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
    at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
    Caused by: java.lang.ClassCastException: org.gradle.api.internal.tasks.DefaultTaskInputs$TaskInputUnionFileCollection cannot be cast to org.gradle.api.in
    ternal.file.collections.DefaultConfigurableFileCollection
    at com.android.build.gradle.tasks.factory.UnitTestConfigAction.fixTestTaskSources(UnitTestConfigAction.java:168)
    at com.android.build.gradle.tasks.factory.UnitTestConfigAction.execute(UnitTestConfigAction.java:92)
    at com.android.build.gradle.tasks.factory.UnitTestConfigAction.execute(UnitTestConfigAction.java:49)
    at org.gradle.api.internal.tasks.DefaultTaskContainer.create(DefaultTaskContainer.java:128)
    at com.android.build.gradle.internal.TaskContainerAdaptor.create(TaskContainerAdaptor.java:59)
    at com.android.build.gradle.internal.scope.AndroidTaskRegistry.create(AndroidTaskRegistry.java:46)
    at com.android.build.gradle.internal.scope.AndroidTaskRegistry.create(AndroidTaskRegistry.java:81)
    at com.android.build.gradle.internal.TaskManager.createRunUnitTestTask(TaskManager.java:1529)
    at com.android.build.gradle.internal.TaskManager.createUnitTestVariantTasks(TaskManager.java:1276)
    at com.android.build.gradle.internal.VariantManager.createTasksForVariantData(VariantManager.java:450)
    at com.android.build.gradle.internal.VariantManager$3.call(VariantManager.java:291)
    at com.android.build.gradle.internal.VariantManager$3.call(VariantManager.java:288)
    at com.android.builder.profile.ThreadRecorder$1.record(ThreadRecorder.java:55)
    at com.android.builder.profile.ThreadRecorder$1.record(ThreadRecorder.java:47)
    at com.android.build.gradle.internal.profile.SpanRecorders.record(SpanRecorders.java:51)
    at com.android.build.gradle.internal.VariantManager.createAndroidTasks(VariantManager.java:287)
    at com.android.build.gradle.BasePlugin$12.call(BasePlugin.java:661)
    at com.android.build.gradle.BasePlugin$12.call(BasePlugin.java:658)
    at com.android.builder.profile.ThreadRecorder$1.record(ThreadRecorder.java:55)
    at com.android.builder.profile.ThreadRecorder$1.record(ThreadRecorder.java:47)
    at com.android.build.gradle.BasePlugin.createAndroidTasks(BasePlugin.java:657)
    at com.android.build.gradle.BasePlugin$10$1.call(BasePlugin.java:571)
    at com.android.build.gradle.BasePlugin$10$1.call(BasePlugin.java:568)
    at com.android.builder.profile.ThreadRecorder$1.record(ThreadRecorder.java:55)
    at com.android.builder.profile.ThreadRecorder$1.record(ThreadRecorder.java:47)
    at com.android.build.gradle.BasePlugin$10.execute(BasePlugin.java:567)
    at com.android.build.gradle.BasePlugin$10.execute(BasePlugin.java:564)
    at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:93)
    at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:82)
    at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:44)
    at org.gradle.internal.event.BroadcastDispatch.dispatch(BroadcastDispatch.java:79)
    at org.gradle.internal.event.BroadcastDispatch.dispatch(BroadcastDispatch.java:30)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at com.sun.proxy.$Proxy13.afterEvaluate(Unknown Source)
    at org.gradle.configuration.project.LifecycleProjectEvaluator.notifyAfterEvaluate(LifecycleProjectEvaluator.java:67)
    ... 52 more

BUILD FAILED

Total time: 1.675 secs

#46 楼 @anyin
我测试时一直都是用 AS 直接运行的,从命令行编译的只是在最开始试过,后来没有用过了,这里的错误(坑)我没有碰到(踩)过,我的建议是:检查命令行的 gradle 版本是否与 AS 一致,另外检查 AS 是否有设置代理,更多错误信息还是搜索一下吧,gradle Caused by: java.lang.ClassCastException

@Lihuazhang 您好,我这里没有测试没有问题,但是没有 instrumentation,请问是什么原因呢?

@Lihuazhang 请问一下 如何后台执行,拔掉 usb 后执行不中断,类似于 Uiautomator1 中的 --nohup

恒温 #51 · 2017年03月01日 Author

adb shell nohup am instrument -w ...

定义的计算器包名常量 BASIC_SAMPLE_PACKAGE,启动时没有使用呢?

To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/2.14.1/userguide/gradle_daemon.html.

FAILURE: Build failed with an exception.

  • What went wrong:
    Unable to start the daemon process.
    This problem might be caused by incorrect configuration of the daemon.
    For example, an unrecognized jvm option is used.
    Please refer to the user guide chapter on the daemon at https://docs.gradle.org/2.14.1/userguide/gradle_daemon.html

    Please read the following process output to find out more:

    Error occurred during initialization of VM
    Could not reserve enough space for object heap
    Error: Could not create the Java Virtual Machine.
    Error: A fatal exception has occurred. Program will exit.

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

运行 gradlew.bat cC 是 ,报不能吊起虚拟机,怎么解决啊

您好,我出现这样的问题弄了好久都没办法解决,请问能帮我看看吗?谢谢
junit.framework.AssertionFailedError: Exception in constructor: testDemo (java.lang.RuntimeException: Stub!
at com.android.uiautomator.testrunner.UiAutomatorTestCase.(UiAutomatorTestCase.java:5)
at com.example.administrator.myapplication.TestCalculator.(TestCalculator.java:10)
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:430)
at junit.framework.TestSuite.createTest(TestSuite.java:61)
at junit.framework.TestSuite.addTestMethod(TestSuite.java:294)
at junit.framework.TestSuite.addTestsFromTestCase(TestSuite.java:150)
at junit.framework.TestSuite.(TestSuite.java:129)
at android.support.test.internal.runner.junit3.NonLeakyTestSuite.(NonLeakyTestSuite.java:34)
at android.support.test.internal.runner.junit3.AndroidTestSuite.(AndroidTestSuite.java:49)
at android.support.test.internal.runner.junit3.AndroidJUnit3Builder.runnerForClass(AndroidJUnit3Builder.java:52)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runner.Computer.getRunner(Computer.java:40)
at org.junit.runner.Computer$1.runnerForClass(Computer.java:31)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.runners.model.RunnerBuilder.runners(RunnerBuilder.java:101)
at org.junit.runners.model.RunnerBuilder.runners(RunnerBuilder.java:87)
at org.junit.runners.Suite.(Suite.java:81)
at org.junit.runner.Computer.getSuite(Computer.java:28)
at android.support.test.internal.runner.TestRequestBuilder.classes(TestRequestBuilder.java:789)
at android.support.test.internal.runner.TestRequestBuilder.build(TestRequestBuilder.java:753)
at android.support.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:354)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:260)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1932)
)
at junit.framework.Assert.fail(Assert.java:50)
at junit.framework.TestSuite$1.runTest(TestSuite.java:97)
at junit.framework.TestCase.runBare(TestCase.java:134)
at junit.framework.TestResult$1.protect(TestResult.java:115)
at android.support.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:77)
at junit.framework.TestResult.run(TestResult.java:118)
at android.support.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:55)
at junit.framework.TestCase.run(TestCase.java:124)
at android.support.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:63)
at junit.framework.TestSuite.runTest(TestSuite.java:243)
at junit.framework.TestSuite.run(TestSuite.java:238)
at android.support.test.internal.runner.junit3.DelegatingTestSuite.run(DelegatingTestSuite.java:103)
at android.support.test.internal.runner.junit3.AndroidTestSuite.run(AndroidTestSuite.java:69)
at android.support.test.internal.runner.junit3.JUnit38ClassRunner.run(JUnit38ClassRunner.java:103)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:59)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:262)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1932)

Tests ran to completion.

我的 build.gradle 是这样的

apply plugin: 'com.android.application'

android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.example.administrator.myapplication"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha7'
testCompile 'junit:junit:4.12'
compile files('libs/uiautomator.jar')
}

charles0427 UiAutomator2 Server 运行原理分析 中提及了此贴 07月05日 14:50

用命令执行才能生成报告,如何使用 gradlew 命令 或者 am insturment 来运行指定文件夹里的 case?我用这个命令:gradlew connectedAndroidTest 运行的是所有测试类,但是我的框架分两个文件夹,一个回归,一个性能,如果指定文件夹呢?

恒温 [该话题已被删除] 中提及了此贴 04月23日 23:10
l_smile [该话题已被删除] 中提及了此贴 05月19日 22:47

请问有没有 uiautomator 脱离 PC(apk 启动)的教程???

xinxi Android Uiautomator2 脱机 Monkey 测试 中提及了此贴 04月24日 22:26
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册