如何在测试执行期间访问assets文件夹中的文件?

23
如何在执行单元测试期间从资产文件夹访问文件?我的项目使用Gradle构建,我使用Robolectric运行测试。似乎Gradle正在识别assetsenter image description here 这是我努力阅读文件的方式:
public String readFileFromAssets(String fileName) throws IOException {
    InputStream stream = getClass().getClassLoader().getResourceAsStream("assets/" + fileName);
    Preconditions.checkNotNull(stream, "Stream is null");
    BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
    return IOUtils.toString(reader);
}

但是stream始终为null。我尝试了很多不同的方法,比如使用不同的方式定义文件路径。

非常感谢您提前的帮助。


这适用吗?http://stackoverflow.com/questions/20184480/loading-assets-in-an-android-test-project - Peter Niederwieser
不,它不是InstrumentationTestCase。 - Eugene
也许它需要这样。 - Peter Niederwieser
我从未使用过单元测试,但可能与我的答案相同:https://dev59.com/pmkw5IYBdhLWcg3w8fBJ#9544781 访问资产文件夹不能直接进行,您必须使用getAssets()才能使其正常工作。然而,由于我从未使用过单元测试,我的评论可能不相关。如果是这种情况,请告诉我,我会将其删除。 - HpTerm
请查看此问题:https://dev59.com/qmAf5IYBdhLWcg3wcyer - Qw4z1
9个回答

16

我遇到了相同的问题,这是对我起作用的方法。

我将测试文件放置在 src/test/resources 文件夹中而不是 assets 文件夹。

然后我以以下方式获取这些文件的流。

private InputStream openFile(String filename) throws IOException {
       return getClass().getClassLoader().getResourceAsStream(filename);
}
< p > < code >filename 是相对于 < code > resources 文件夹内文件的路径。

就是这样。 我在 Robolectric github 上找到了解决方案。


16
基本上,您必须使用Context来读取资源。您不能使用ClassLoader加载资源,因为它不在类路径中。我不确定您如何运行Robolectric测试用例。以下是我在Android Studio和gradle命令中实现的方法。
我添加了一个单独的app-unit-test模块来运行应用程序项目中的Robolectric测试用例。通过适当的构建配置和自定义RobolectricTestRunner,以下测试用例将通过。
@Config
@RunWith(MyRobolectricTestRunner.class)
public class ReadAssetsTest {

    @Test
    public void test_ToReadAssetsFileInAndroidTestContext() throws IOException {

        ShadowApplication application = Robolectric.getShadowApplication();
        Assert.assertNotNull(application);
        InputStream input = application.getAssets().open("b.xml");
        Assert.assertNotNull(input);
    }

}

app-unit-test/build.gradle

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.14.1'
    }
}

apply plugin: 'java'
evaluationDependsOn(':app')

sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7

repositories {
    maven { url "$System.env.ANDROID_HOME/extras/android/m2repository" } // Fix 'com.android.support:*' package not found issue
    mavenLocal()
    mavenCentral()
    jcenter()
}

dependencies {
    testCompile 'junit:junit:4.8.2'
    testCompile('org.robolectric:robolectric:2.4') {
        exclude module: 'classworlds'
        exclude module: 'commons-logging'
        exclude module: 'httpclient'
        exclude module: 'maven-artifact'
        exclude module: 'maven-artifact-manager'
        exclude module: 'maven-error-diagnostics'
        exclude module: 'maven-model'
        exclude module: 'maven-project'
        exclude module: 'maven-settings'
        exclude module: 'plexus-container-default'
        exclude module: 'plexus-interpolation'
        exclude module: 'plexus-utils'
        exclude module: 'wagon-file'
        exclude module: 'wagon-http-lightweight'
        exclude module: 'wagon-provider-api'
        exclude group: 'com.android.support', module: 'support-v4'
    }
    testCompile('com.squareup:fest-android:1.0.+') {
        exclude group: 'com.android.support', module: 'support-v4'
    }
    testCompile 'org.mockito:mockito-core:1.10.10'
    def appModule = project(':app')
    testCompile(appModule) {
        exclude group: 'com.google.android'
        exclude module: 'dexmaker-mockito'
    }
    testCompile appModule.android.applicationVariants.toList().first().javaCompile.classpath
    testCompile appModule.android.applicationVariants.toList().first().javaCompile.outputs.files
    testCompile 'com.google.android:android:4.1.1.4'
    /* FIXME : prevent Stub! error
        testCompile files(appModule.plugins.findPlugin("com.android.application").getBootClasspath())
        */
    compile project(':app')
}

添加自定义的RobolectricTestRunner以调整文件路径。 查看资产路径。
public class MyRobolectricTestRunner extends RobolectricTestRunner {

    private static final String APP_MODULE_NAME = "app";

    /**
     * Creates a runner to run {@code testClass}. Looks in your working directory for your AndroidManifest.xml file
     * and res directory by default. Use the {@link org.robolectric.annotation.Config} annotation to configure.
     *
     * @param testClass the test class to be run
     * @throws org.junit.runners.model.InitializationError if junit says so
     */
    public MyRobolectricTestRunner(Class<?> testClass) throws InitializationError {
        super(testClass);
        System.out.println("testclass="+testClass);
    }

    @Override
    protected AndroidManifest getAppManifest(Config config) {

        String userDir = System.getProperty("user.dir", "./");
        File current = new File(userDir);
        String prefix;
        if (new File(current, APP_MODULE_NAME).exists()) {
            System.out.println("Probably running on AndroidStudio");
            prefix = "./" + APP_MODULE_NAME;
        }
        else if (new File(current.getParentFile(), APP_MODULE_NAME).exists()) {
            System.out.println("Probably running on Console");
            prefix = "../" + APP_MODULE_NAME;
        }
        else {
            throw new IllegalStateException("Could not find app module, app module should be \"app\" directory in the project.");
        }
        System.setProperty("android.manifest", prefix + "/src/main/AndroidManifest.xml");
        System.setProperty("android.resources", prefix + "/src/main/res");
        System.setProperty("android.assets", prefix + "/src/androidTest/assets");

        return super.getAppManifest(config);
    }

}

我按照这个博客的指示去做了。 完整的示例代码可以在这里找到。

7

通过最新的Android仪器测试,您只需使用以下代码:

InstrumentationRegistry.getContext().getAssets().open(filePath);

这个 InstrumentationRegistry 已经被弃用了,所以这不再是正确的语法。 - Joakim Engstrom
androidx.test.InstrumentationRegistry 的新替代品是 androidx.test.platform.app.InstrumentationRegistry,请参见此处:https://developer.android.com/reference/androidx/test/platform/app/InstrumentationRegistry.html。 - JuanMoreno

7

更新至Roboelectric 3.1版本

    @Test
    public void shouldGetJSONFromAsset() throws Exception{
         Assert.assertNotNull(RuntimeEnvironment.application); //Getting the application context
         InputStream input = RuntimeEnvironment.application.getAssets().open("fileName.xml");// the file name in asset folder
         Assert.assertNotNull(input);
        }

另请参阅

  1. 2.4 到 3.0 升级指南
  2. 3.0 到 3.1 升级指南

de 指南


1
对我不起作用,RuntimeEnvironment.application.getAssets().open 返回一个 IOException,我将 assets 文件夹添加为:https://gist.github.com/nebiros/4d6370c3ba87f3dd6f6a。 - nebiros
对我来说没问题。然而,这会在主源集的资产文件夹中打开文件,而不是测试文件夹(这正是我要找的)。 - Bao-Long Nguyen-Trong

1

当使用 Robolectric 4.0 之前的版本时,我们需要考虑以下内容:

默认情况下,Robolectric 假定您的资源和资产位于名为 res 和 assets 的目录中。这些路径被假定为相对于清单所在的目录。您可以通过向 @Config 注释添加 resourceDir 和 assetDir 选项来更改这些值。

请参阅官方 文档


这在当前版本(4.0)中已不再适用。 - WindRider

1
如果您正在使用Kotlin,您可以像这样做:
getInstrumentation().targetContext.assets.open("news.json")

可以使用targetContext而不是.context


0

尝试将其添加到您的应用程序 build.gradle 中的 sourceSets 中

android {

    sourceSets {
        test.resources.srcDirs += 'src/androidTest/assets'
    }
}

0

这样它们会编译进我需要避免的应用程序中。 - Eugene

-1
如果一切正确,那么你需要像这样读取它:
public String readFileFromAssets(String fileName, Context context) throws IOException {
    InputStreamReader stream = new InputStreamReader(context.getAssets().open(fileName));
    Preconditions.checkNotNull(stream, "Stream is null");
    BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
    return IOUtils.toString(reader);
}

你需要传递Context才能使它正常工作。

另一件需要验证的事情是你是否正确配置了Gradle中的资源?这里只是一个例子:

sourceSets {
    main {
        java.srcDirs = ['src/main']
        // can be configured in different way
        assets.srcDirs = ['src/androidTest/assets']
        // other things, examples
        res.srcDirs = ['res']
        manifest.srcFile 'AndroidManifest.xml'
    }
}

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