35

For my Android app I'm writing unit tests that require reading some files. Since those are test-only files I don't want them in my res folders as I don't want them to end up in my final .apk file.

I want to do something similar to this question but using the newly added (in Gradle 1.1) unit test support (as opposed to instrumentation test).

My project structure is as follows:

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

what should my myClassTest test look like to be able to successfully read the testFile.txt?

Community
  • 1
  • 1
Krzysztof Kozmic
  • 27,267
  • 12
  • 73
  • 115
  • I think we'd need to see more code for myClass.java to comment on this. You want to have the test to pass the testFile.txt to whichever method processes text files in myClass and then check (assert with AssertJ) that the result comes back as expected. Looking into Robolectric and Mockito may give you some ideas. – TTransmit Mar 12 '15 at 15:09
  • `myClass.java` is irrelevant. Assume it has a method that consumes an `InputStream` and I want the stream to contain what's in the file in my test. Does that make sense? – Krzysztof Kozmic Mar 13 '15 at 07:09
  • if you want to do some hack about that, then read [My Post](http://stackoverflow.com/questions/28206507/getting-list-of-subfolders-name-and-files-name-in-android-source-package). you can open the apk inside your mobile then read it as stream – Randyka Yudhistira Mar 19 '15 at 07:27

6 Answers6

61

At the time when this question was asked this simply wasn't working. Fortunately this has been fixed since.

You have to put your text file under the app/src/test/resources folder as the OP was trying to do. Additionally it has to be in the same package as your test class. So if you have ReadFileTest.java in package com.example.test in app/src/test/java folder, then your test file should be in app/src/test/resources/com/example/test.

test folder structure

Then you can get to your text file like this:

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

This opens an InputStream for the text file. If you're not sure what do with it, here are some of the many ways you could use it: Read/convert an InputStream to a String

Community
  • 1
  • 1
Marcin Koziński
  • 10,835
  • 3
  • 47
  • 61
  • FWIW I think this works now no problem. If there is any interest I could double check and edit the answer to show how the file can be loaded. – Marcin Koziński Feb 19 '16 at 23:15
  • Yes I have interest, please demonstrate. – Tyler Pfaff Mar 29 '16 at 22:19
  • 2
    I'd upvote this 100 times if I could. The key really is getting the paths right. Thank you, thank you! :D – JRG-Developer May 27 '17 at 23:39
  • 8
    if somebody has problem to have `InputStream` result _null_, add slash such as `getClass().getResourceAsStream("/testFile.txt")`. – Youngjae Feb 04 '18 at 15:01
  • 3
    one more tip for those who are stranded with null for the input stream, make sure not to add dots while creating the directory path for resources (eg com.example.test). Android studio will auto create the directory inside the java folder when created with dots but not for resources directory – Ismail Iqbal Mar 05 '19 at 09:12
  • 4
    in Kotlin, this.javaClass.getResourceAsStream("testFile.txt") – user1960422 Apr 14 '20 at 01:50
  • You saved my day Ismail. Maybe @MarcinKoziński could add both voted comments to his answer. – JacksOnF1re Jul 08 '20 at 14:06
  • For Kotlin, using `this.javaClass.getResourceAsStream("testFile.txt")` didn't work for me. Instead, `ReadFileTest::class.java.getResourceAsStream("testFile.txt"))` worked – marius bardan Jul 11 '20 at 14:24
  • FFS man someone should give you an award or something. The only solution out of 50 that worked. Thanks – Lheonair Sep 22 '21 at 21:07
19

Add this to your build.gradle:

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

For resources to be accessible by unit tests, add your files in: src/test/resources. And for instrumentation tests, add your files in: src/androidTest/resources.

Deepanshu
  • 351
  • 1
  • 3
  • 8
  • I had a problem using `java.nio.file.Paths.get("foo", "bar")` to read json files on Android tests, because Paths was pointing to `.idea/foo/bar`, and using `test {resources.srcDirs += ['src/test/resources']}` saved my day, Thanks a lot!!! – Hermandroid Sep 03 '20 at 22:59
12

Followed by @Deepansu's answer, I unified test data for both Test and AndroidTest in {project root}/sampledata directory which is a default location of Android Studio New > Sample Data Directory command.

1. In your project, right-click and click New > Sample Data Directory. This will create sampledata directory in app, which has same hierarchy as build.gradle file, src and build directories.

2. In build.gradle, add scripts as below;

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

3. Sync in gradle.

Now, we can put test resource files in one directory and use them in both test environment.

You can read file as below;

// 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();
}
Youngjae
  • 24,352
  • 18
  • 113
  • 198
2

A better and more general appraoch is the following. It is applicable also in other project types, like Spring. Another benefit is that you don't have to put the file specifically at a place in the package structure. This dependency should not be made (on the package structure). It makes it more readable if you have a good filename (with test folder structure).

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

An example:

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;
    }
}
tm1701
  • 7,307
  • 17
  • 79
  • 168
1

I am working a project with an structure similar to what you mentioned. I put all my server responses in a file under resources folder like app/src/test/resources/BookingInfo.json.

All java test files are under app/src/test/java/PACKAGE_NAME similar to what you said. I have a Fixture class under a package that contains name of resources:

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

And finally a FixtureUtils class that a method of this class is responsible to read resource file and return result.

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

And AFixture class looks like:

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

}

Note: java.nio.file has been removed from JDK8 if I'm not mistaken however you have no problem if you are using JDK7. If you are using JDK8 then you just need to change stringFromAsset method in such a way like FileInputStream (old fashion style) or Scanner.

Community
  • 1
  • 1
Hesam
  • 52,260
  • 74
  • 224
  • 365
-3

An example of a correct way to do this would be to put the file in your assets folder. However, the contents of the assets folder will be added to the apk.

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

You can cheat this system and traverse to any other file in your project. Be sure to create an assets directory in the location specified in the project's iml file (e.g. src/main/assets).

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

An example of a way to get resources would be:

Resources resources = new Activity().getResources();
TTransmit
  • 3,270
  • 2
  • 28
  • 43