Robolectric与Gradle一起使用:找不到资源

31

我想要在最新的Gradle Android构建系统中运行Robolectric测试,但我卡在了访问主项目资源的地方。

为了避免javaandroid Gradle插件之间的冲突,我将构建分成了两个独立的项目,因此目录结构大致如下:

.
├── build.gradle
├── settings.gradle
├── mainproject
│   ├── build
│   │   ├── classes
│   │   │   └── debug
│   ├── build.gradle
│   └── src
│       └── main
│           ├── AndroidManifest.xml
│           └── ...
└── test
    ├── build.gradle
    └── src
        └── test
            └── java
                └── ...
                    └── test
                        ├── MainActivityTest.java
                        ├── Runner.java
                        ├── ServerTestCase.java
                        └── StatusFetcherTest.java

我在 test/ 目录下的 build.gradle 文件当前是这样的:

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.stanfy.android:gradle-plugin-java-robolectric:2.0'
    }
}

apply plugin: 'java-robolectric'

repositories {...}

javarob {
    packageName = 'com.example.mainproject'
}

test {
    dependsOn ':mainproject:build'
    scanForTestClasses = false
    include "**/*Test.class"
    // Oh, the humanity!
    def srcDir = project(':mainproject').android.sourceSets.main.java.srcDirs.toArray()[0].getAbsolutePath()
    workingDir srcDir.substring(0, srcDir.lastIndexOf('/'))
}

project(':mainproject').android.sourceSets.main.java.srcDirs.each {dir ->
    def buildDir = dir.getAbsolutePath().split('/')
    buildDir =  (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')

    sourceSets.test.compileClasspath += files(buildDir)
    sourceSets.test.runtimeClasspath += files(buildDir)
}

dependencies {    
    testCompile group: 'com.google.android', name: 'android', version: '4.1.1.4'
    testCompile group: 'org.robolectric', name: 'robolectric', version: '2.0-alpha-3'
    ...
}
恶意的classpath魔法技巧让我可以访问主项目的所有类,除了R。在构建目录中存在.class文件,但在执行compileTestJava任务时会引发此错误:

恶意的classpath魔法技巧让我可以访问主项目的所有类,除了R。在构建目录中存在.class文件,但在执行compileTestJava任务时会引发此错误:

/.../MainActivityTest.java:16: error: cannot find symbol
                final String appName = activity.getResources().getString(R.string.app_name);
                                                                          ^
  symbol:   variable string
  location: class R
1 error
:test:compileTestJava FAILED

使用新的构建系统执行Robolectric测试一定有更好的方法,对吧?

(完整应用程序源代码)


2
我认为这不会起作用。Robolectric 中还有一些其他内部问题,关于如何找到实际资源的位置,这将无法与 Gradle 插件所做的项目依赖项相适应。我需要与 Robolectric 开发人员合作,使其兼容。这是我要做的事情清单上的一项。 - Xavier Ducrohet
那么,在新的Android Studio IDE中,是否已经有一种方法可以使用Gradle运行Robolectric了呢? - Imanol
@Xav,有没有可能通过Gradle很快让它工作?你能指出Robolectric的github页面上的问题吗? - Snicolas
2个回答

27

我遇到了同样的问题,并想出了一个解决方法。我没有为测试创建一个单独的项目,而是创建了一个用于Robolectric测试的源集,并添加了一个新任务,使"check"依赖该任务。以下是(有效)构建文件的相关部分,其中使用了您问题中的一些代码:

Instead of creating a separate project for the tests, I created a source set for the Robolectric tests and added a new task that "check" would depend on. Using some of the code from your question, here are the relevant bits of the (working) build file:

apply plugin: 'android'

sourceSets {
    testLocal {
        java.srcDir file('src/test/java')
        resources.srcDir file('src/test/resources')
    }
}

dependencies {
    compile 'org.roboguice:roboguice:2.0'
    compile 'com.google.android:support-v4:r6'

    testLocalCompile 'junit:junit:4.8.2'
    testLocalCompile 'org.robolectric:robolectric:2.1'
    testLocalCompile 'com.google.android:android:4.0.1.2'
    testLocalCompile 'com.google.android:support-v4:r6'
    testLocalCompile 'org.roboguice:roboguice:2.0'
}

task localTest(type: Test, dependsOn: assemble) {
    testClassesDir = sourceSets.testLocal.output.classesDir

    android.sourceSets.main.java.srcDirs.each { dir ->
        def buildDir = dir.getAbsolutePath().split('/')
        buildDir =  (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')

        sourceSets.testLocal.compileClasspath += files(buildDir)
        sourceSets.testLocal.runtimeClasspath += files(buildDir)
    }

    classpath = sourceSets.testLocal.runtimeClasspath
}

check.dependsOn localTest

我包含了依赖块,以指出为了让这个项目工作,我不得不在自定义的testLocal源集中重复所有compile依赖项。

运行gradle testLocal可以构建并运行src/test/java中的测试,而运行gradle check可以除了默认的android instrumentTest源集之外还运行这些测试。

希望这能帮到你!


1
这个问题似乎是由于测试运行程序无法找到位于src/main路径下的 AndroidManifest.xml文件引起的。我想我可以在RobolectricTestRunner 的子类中设置这些路径,但在Eclipse中,我只需将工作目录设置为主项目即可。 那么在Gradle文件中是否也能这样做呢? - passy
1
@passy 很抱歉我没有遇到过这个问题。不过,你应该能够将你的 AndroidManifest.xml 放在项目目录的根目录中,并通过在 build.gradle 中执行类似以下操作来覆盖清单源集:android { sourceSets { main { manifest.srcFile 'AndroidManifest.xml' } } } 来源:http://tools.android.com/tech-docs/new-build-system/user-guide - user2457888
3
再次感谢@user2457888。 :) 我把localTest任务中的workingDir设置为src/main,现在它可以找到AndroidManifest.xml和所有资源文件了。 - passy
1
@alex - 我找到解决方法了!进入模块设置,在依赖项中添加一个新库,选择从maven添加robolectric。将其范围更改为编译。现在可以工作了,但如果您也使用instrumentationTest可能会产生干扰。 - Mark
有什么想法可以更新这个项目,将尚未成为标准的aar格式作为依赖项包含进来吗?请参见我的相关问题:https://dev59.com/n3vaa4cB1Zd3GeqPA0Hz - Colin M.
显示剩余9条评论

8

更新: Jake Wharton刚刚宣布了gradle-android-test-plugin。您可以在https://github.com/square/gradle-android-test-plugin找到它。

如果您计划使用robolectric,它似乎非常简化。


旧答案如下

robolectric-plugin看起来很有前途。

他们提供的示例build.gradle文件是:

buildscript {
    repositories {
        mavenCentral()
        maven {
            url "https://oss.sonatype.org/content/repositories/snapshots"
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.4.2'
        classpath 'com.novoda.gradle:robolectric-plugin:0.0.1-SNAPSHOT'
    }
}

apply plugin: 'android'
apply plugin: 'robolectric'

repositories {
    mavenCentral()
    mavenLocal()
    maven {
        url "https://oss.sonatype.org/content/repositories/snapshots"
    }
}

dependencies {
    //compile files('libs/android-support-v4.jar')

    // had to deploy to sonatype to get AAR to work
    compile 'com.novoda:actionbarsherlock:4.3.2-SNAPSHOT'

    robolectricCompile 'org.robolectric:robolectric:2.0'
    robolectricCompile group: 'junit', name: 'junit', version: '4.+'
}

android {
    compileSdkVersion 17
    buildToolsVersion "17.0.0"

    defaultConfig {
        minSdkVersion 7
        targetSdkVersion 17
    }
}

看起来它无法与Android Gradle插件版本0.5一起使用,但也许很快就会有解决方法。


我急切地等待着Jake的插件支持按buildType分离资源,但目前它似乎无法处理。 有人写了一个补丁来修复这个问题(几个月前),但还没有得到评论并且未被接受。 在此之前,已经采纳的答案中的方法正在发挥作用。 - Colin M.
2
看起来Jake Wharton的插件现在已经过时了? - Flame
是的,没错。似乎有一股热潮转向标准的Android测试基础设施。Robolectric在某些情况下仍然很有用,但我发现更容易管理应用程序,使大多数纯Java类成为Java库模块,并使用JUnit进行单元测试以及使用Android测试框架进行特定于Android的测试。 - Saad Farooq
旧版和新版答案都参考了已被弃用的插件。 - Cris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接