How to read input files in maven junit

Sometimes we need to put unit test data into plain text files. For example, assume we want to test a parser using a json string as the test data. If we put the json string as a constant string in the java code, we end up with a lot of error-prone escaping characters. In that case, we may want to put the test string into a file as a resource and read the string from the file in junit.

In maven, we need to put the resource file in src/test/resources. Let me create a demo from scratch.

> mvn archetype:create -DgroupId=org.fuyun -DartifactId=junitresdemo
> find .
.
./junitresdemo
./junitresdemo/pom.xml
./junitresdemo/src
./junitresdemo/src/test
./junitresdemo/src/test/java
./junitresdemo/src/test/java/org
./junitresdemo/src/test/java/org/fuyun
./junitresdemo/src/test/java/org/fuyun/AppTest.java
./junitresdemo/src/main
./junitresdemo/src/main/java
./junitresdemo/src/main/java/org
./junitresdemo/src/main/java/org/fuyun
./junitresdemo/src/main/java/org/fuyun/App.java

> cd junitresdemo
> mkdir -p src/test/resources
> vi src/test/resources/myres.txt
> cat src/test/resources/myres.txt
test1=testdata

As you can see, I put my test data as a key-value pair in a property file. If your test data contains special characters such as escape char, you’d better handle file reading by yourself instead of using Properties as I am going to show.

Then I modify the automatically generated test code src/test/java/org/fuyun/AppTest.java as follows.

package org.fuyun;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

import java.io.InputStream;
import java.util.Properties;

public class AppTest extends TestCase {
    public AppTest( String testName ) {
        super( testName );
    }

    public static Test suite() {
        return new TestSuite( AppTest.class );
    }

    public void testApp() throws java.io.IOException {
        InputStream in =
            getClass().getClassLoader().getResourceAsStream("myres.txt");
        Properties p = new Properties();
        p.load(in);
        String mystr = p.getProperty("test1");
        assertEquals("testdata", mystr);
    }
}

To read the property file, we need to use getResourceAsStream. Actually this is why I want to write this blog. If you search on web, you may find that people talk about you can load the file using Class.getResourceAsStream(). So, I am supposed to write the line as

        InputStream in = getClass().getResourceAsStream("myres.txt");

It can compile. But the test will fail. The InputStream variable in will be null, i.e., it cannot find myres.txt. Why? Why do we have to use the method defined in ClassLoader?

The difference between Class.getResourceAsStream and ClassLoader.getResourceAsStream is that Class.getResourceAsStream attempts to first resolve the file name by appending the package prefix (org/fuyun/) if the file name is not an absolute path, otherwise removes the leading “/” if the path is absolute. Then, it calls the ClassLoader’s getResourceAsStream to load the resolved file name. This is documented here.

For example, if I do the following hack, the test will pass temporarily.

> mv target/test-classes/myres.txt target/test-classes/org/fuyun/.
> mvn test

But to really fix it, we should revise the line by adding a leading “/” in the file name as follows.

        InputStream in = getClass().getResourceAsStream("/myres.txt");

On the other hand, if you use ClassLoader.getResourceAsStream, the leading “/” will make it unable to find the file.

8 thoughts on “How to read input files in maven junit

  1. I had a similar issue running my test from eclipse. But did not have any problem looking up a property file from test resources when I ran maven command line.

    I had to run maven build from command once, which copied the file from test resource to target/test-classes. (this however requires that you have your copy-resources setup correctly).

    From that point on my test-case was able to pick up the properties file from command line as well as Eclipse.

  2. Pingback: TODD

  3. Also you can add section to maven(pom.xml) like this:

    ${basedir}/src/test/…/resource

    *.txt

    and open resource:
    getClass().getClassLoader().getResourceAsStream( “myres.txt” )

  4. Great Post, Even better poped in the top 5 on google. This probably saved me couple of hours