如何在Android单元测试中读取仅测试文件

时间:2015-03-08 06:23:58

标签: android unit-testing android-studio gradle

对于我的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

6 个答案:

答案 0 :(得分:34)

在提出这个问题时,这根本不起作用。幸运的是,这已经得到修复。

您必须将文本文件放在{I}尝试执行的app/src/test/resources文件夹下。此外,它必须与您的测试类位于同一个包中。因此,如果ReadFileTest.java文件夹中的com.example.test包中有app/src/test/java,则您的测试文件应位于app/src/test/resources/com/example/test

test folder structure

然后你可以这样找到你的文本文件:

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

这会为文本文件打开InputStream。如果您不确定如何使用它,可以使用以下几种方法:Read/convert an InputStream to a String

答案 1 :(得分:6)

将此添加到build.gradle:

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

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

答案 2 :(得分:2)

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

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

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

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

3。 Sync

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

您可以阅读以下文件;

// 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();
}

答案 3 :(得分:1)

我正在开展一个类似于你提到的结构的项目。 我将所有服务器响应放在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,那么您只需要以FileInputStream(旧时尚风格)或Scanner等方式更改stringFromAsset方法。

答案 4 :(得分:0)

以下是一种更好且更通用的方法。它也适用于其他项目类型,例如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;
    }
}

答案 5 :(得分:-2)

执行此操作的正确方法的示例是将文件放在assets文件夹中。但是,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();