【问题标题】:How to access files from assets folder during tests execution?如何在测试执行期间访问资产文件夹中的文件?
【发布时间】:2014-11-07 15:33:47
【问题描述】:

如何在单元测试执行期间访问资产文件夹中的文件?我的项目是使用 Gradle 构建的,我使用 Robolectric 运行测试。似乎 gradle 正在识别 assets:

这就是我努力阅读文件的方式:

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。我尝试了很多不同的方法,即使用不同的方法定义文件的路径。

非常感谢您。

【问题讨论】:

标签: android gradle robolectric android-assets


【解决方案1】:

我刚刚遇到了同样的问题,这就是它对我的工作方式。

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

然后我通过以下方式将这些文件作为流获取

private InputStream openFile(String filename) throws IOException {
       return getClass().getClassLoader().getResourceAsStream(filename);
}

filenameresources 文件夹内文件的相对路径。

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

【讨论】:

    【解决方案2】:

    基本上你必须使用Context 来读取资产。您不能使用 ClassLoader 加载资产,因为它不在类路径中。我不确定您如何运行 Robolectric 测试用例。以下是我可以在 Android Studio 和 grade 命令中实现的方法。

    我添加了单独的 app-unit-test 模块来在 app 项目中运行 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);
        }
    
    }
    

    我是按照这个博客去做的。

    完整的示例代码是here

    【讨论】:

      【解决方案3】:

      更新至 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 to 3.0 Upgrade Guide
      2. 3.0 to 3.1 Upgrade Guide

      德指南

      【讨论】:

      • 对我不起作用,RuntimeEnvironment.application.getAssets().open 返回IOException,我将资产文件夹添加为:gist.github.com/nebiros/4d6370c3ba87f3dd6f6a
      • 为我工作。但是,这会打开主源集的资产文件夹中的文件,而不是测试文件(这是我正在寻找的)。
      【解决方案4】:

      使用最新的 Android Instrumentation Test,您可以使用这个:

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

      【讨论】:

      【解决方案5】:

      当 Robolectric 4.0 之前的版本时,我们需要考虑:

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

      见官方docs

      【讨论】:

      • 这在当前版本 (4.0) 中不再实际。
      【解决方案6】:

      您是否尝试将 assets 文件夹放在 main 而不是 androidTest 下?

      另外,您是否使用https://github.com/robolectric/robolectric-gradle-plugin 来运行您的测试?

      【讨论】:

      • 这样他们会在我需要避免的应用程序中编译。
      【解决方案7】:

      如果你使用 Kotlin,你可以有这样的东西:

      getInstrumentation().targetContext.assets.open("news.json")
      

      你可以简单地使用targetContext来代替.context

      【讨论】:

        【解决方案8】:

        如果一切都是正确的,那么你需要这样的东西来阅读它:

        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'
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2014-05-04
          • 1970-01-01
          • 2012-05-13
          • 1970-01-01
          • 2017-06-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-13
          相关资源
          最近更新 更多