【问题标题】:How to read a test-only file in Android unit test如何在 Android 单元测试中读取仅测试文件
【发布时间】:2015-05-09 12:43:41
【问题描述】:

对于我的 Android 应用,我正在编写需要读取一些文件的单元测试。由于这些是仅供测试的文件,我不希望它们出现在我的 res 文件夹中,因为我不希望它们最终出现在我的最终 .apk 文件中。

我想做类似于this question 的事情,但使用新添加的(在 Gradle 1.1 中)unit test support(与仪器测试相反)。

我的项目结构如下:

/app
   /src
      /main
         /java/my.module/myClass.java
         /res/production_resources_go_here
      /test
         /java/my.module/myClassTest.java
         /resources/testFile.txt

我的myClassTest 测试应该是什么样子才能成功读取testFile.txt

【问题讨论】:

  • 我认为我们需要查看 myClass.java 的更多代码才能对此发表评论。您希望测试将 testFile.txt 传递给 myClass 中处理文本文件的任何方法,然后检查(使用 AssertJ 断言)结果是否按预期返回。研究 Robolectric 和 Mockito 可能会给你一些想法。
  • myClass.java 无关紧要。假设它有一个使用InputStream 的方法,并且我希望流包含我的测试中文件中的内容。这有意义吗?
  • 如果您想对此做一些修改,请阅读My Post。您可以在手机中打开 apk,然后将其作为流读取

标签: android unit-testing android-studio gradle


【解决方案1】:

在提出这个问题时,这根本行不通。幸运的是,此问题已得到解决。

您必须将文本文件放在app/src/test/resources 文件夹下,就像 OP 试图做的那样。此外,它必须与您的测试类在同一个包中。因此,如果您在app/src/test/java 文件夹中的包com.example.test 中有ReadFileTest.java,那么您的测试文件应该在app/src/test/resources/com/example/test 中。

然后你可以像这样访问你的文本文件:

getClass().getResourceAsStream("testFile.txt")

这将为文本文件打开一个InputStream。如果您不确定如何使用它,这里有一些您可以使用它的方法:Read/convert an InputStream to a String

【讨论】:

  • FWIW 我认为这现在没问题了。如果有任何兴趣,我可以仔细检查并编辑答案以显示如何加载文件。
  • 如果可以的话,我会投票 100 次。真正的关键是找到正确的路径。谢谢谢谢! :D
  • 如果有人有InputStream结果null的问题,添加斜杠如getClass().getResourceAsStream("/testFile.txt")
  • 对于那些在输入流中被 null 困住的人的另一个提示,确保在创建资源的目录路径时不要添加点(例如 com.example.test)。 Android Studio 会在使用点创建时自动在 java 文件夹内创建目录,但不会为资源目录创建目录
  • 在 Kotlin 中,this.javaClass.getResourceAsStream("testFile.txt")
【解决方案2】:

将此添加到您的 build.gradle:

android {
    sourceSets {
        test {
           resources.srcDirs += ['src/test/resources']
        }
        androidTest {
           resources.srcDirs += ['src/androidTest/resources']
        }
    }
}

要让单元测试可以访问资源,请将您的文件添加到:src/test/resources。 对于仪器测试,请将您的文件添加到:src/androidTest/resources

【讨论】:

  • 我在使用java.nio.file.Paths.get("foo", "bar") 读取Android 测试中的json 文件时遇到问题,因为路径指向.idea/foo/bar,而使用test {resources.srcDirs += ['src/test/resources']} 节省了我的时间,非常感谢!!!跨度>
【解决方案3】:

根据@Deepansu 的回答,我将TestAndroidTest 的测试数据统一在{project root}/sampledata 目录中,这是Android Studio New > Sample Data Directory 命令的默认位置。

1. 在您的项目中,右键单击并单击New > Sample Data Directory。这将在app 中创建sampledata 目录,该目录与build.gradle 文件、srcbuild 目录具有相同的层次结构。

2.build.gradle中,添加如下脚本;

android {
    sourceSets {
        test {
           resources.srcDirs += ['sampledata']
        }
        androidTest {
           resources.srcDirs += ['sampledata']
        }
    }
}

3. Sync 在 gradle 中。

现在,我们可以将测试资源文件放在一个目录中,并在两个测试环境中使用。

您可以读取如下文件;

// use somewhere at test logic. Note that slash symbol is required (or not).
jsonObject = new JSONObject(readFromFile("/testFile.json"));

// a method to read text file.
public String readFromFile(String filename) throws IOException {
    InputStream is = getClass().getResourceAsStream(filename);
    StringBuilder stringBuilder = new StringBuilder();
    int i;
    byte[] b = new byte[4096];
    while ((i = is.read(b)) != -1) {
        stringBuilder.append(new String(b, 0, i));
    }
    return stringBuilder.toString();
}

【讨论】:

    【解决方案4】:

    我正在开展一个与您提到的结构相似的项目。 我将所有服务器响应放在resources 文件夹下的文件中,例如app/src/test/resources/BookingInfo.json

    所有java测试文件都在app/src/test/java/PACKAGE_NAME下,和你说的差不多。我在包含资源名称的包下有一个 Fixture 类:

    @SuppressWarnings("nls")
    public final class Fixtures
    {
        public static final String GET_ANNOUNCEMENT = "GetAnnouncement.json";
    ...
    }
    

    最后是一个 FixtureUtils 类,该类的一个方法负责读取资源文件并返回结果。

    import java.nio.file.Files;
    import java.nio.file.Paths;
    
    public class FixtureUtils
        {
            public static final AFixture fixtureFromName(final String fixtureName)
            {
                final String fixtureString = FixtureUtils.stringFromAsset(fixtureName);
    
                if (StringUtils.isEmpty(fixtureString))
                {
                    return null;
                }
    
                final Gson gson = new Gson();
                final AFixture aFixture = gson.fromJson(fixtureString, AFixture.class);
                return aFixture;
            }
    
            private static final String stringFromAsset(final String filename)
            {
                try
                {
                    final URL resourceURL = ClassLoader.getSystemResource(filename);
                    if (resourceURL == null)
                    {
                        return null;
                    }
    
                    final String result = new String(Files.readAllBytes(Paths.get(resourceURL.toURI())),
                                    Charset.forName("UTF-8")); //$NON-NLS-1$
                    return result;
                }
                catch (final Exception e)
                {
                    e.printStackTrace();
                }
    
                return null;
            }
    
            private FixtureUtils()
            {
                // Ensure Singleton
            }
        }
    

    AFixture 类看起来像:

    public class AFixture
    {
        public List<JsonElement> validItems;
        public List<JsonElement> invalidItems;
    
        public AFixture()
        {
            super();
        }
    
        public List<JsonElement> getInvalidItems()
        {
            return this.invalidItems;
        }
    
        public List<JsonElement> getValidItems()
        {
            return this.validItems;
        }
    
        public void setInvalidItems(final List<JsonElement> invalidItems)
        {
            this.invalidItems = invalidItems;
        }
    
        public void setValidItems(final List<JsonElement> validItems)
        {
            this.validItems = validItems;
        }
    
    }
    

    注意:如果我没记错的话,java.nio.file 已从 JDK8 中删除,但如果您使用的是 JDK7,则没有问题。如果您使用的是 JDK8,那么您只需将 stringFromAsset 方法更改为 FileInputStream(旧时尚风格)或 Scanner

    【讨论】:

      【解决方案5】:

      以下是一种更好、更通用的方法。它也适用于其他项目类型,如 Spring。另一个好处是您不必将文件专门放在包结构中的某个位置。不应该建立这种依赖关系(在包结构上)。如果您有一个好的文件名(带有测试文件夹结构),它会使其更具可读性。

      this.getClass().getClassLoader().getResourceAsStream(filename);
      

      一个例子:

      private String htmlFromTestResourceFile(String filename) {
          try {
              InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(filename);
              return IOUtils.toString(inputStream, "UTF-8");
          } catch( Exception e) {
              e.printStackTrace();
              return null;
          }
      }
      

      【讨论】:

        【解决方案6】:

        执行此操作的正确方法的一个示例是将文件放入您的资产文件夹中。但是,assets 文件夹的内容将被添加到 apk 中。

        InputStream is = resources.getAssets().open("test.txt");
        

        您可以欺骗该系统并遍历您项目中的任何其他文件。请务必在项目的 iml 文件中指定的位置(例如 src/main/assets)创建资产目录。

        InputStream is = resources.getAssets().open("../../test/resources/testFile.txt");
        

        获取资源的一个例子是:

        Resources resources = new Activity().getResources();
        

        【讨论】:

          猜你喜欢
          • 2017-09-11
          • 2016-11-12
          • 1970-01-01
          • 1970-01-01
          • 2015-06-03
          • 2021-08-01
          • 1970-01-01
          • 2017-04-13
          相关资源
          最近更新 更多