0

Oftentimes in my unit tests I have fixtures that are read from resource files and stored into a static attribute on the test class:

public class TestFoo {

    private static String fileContents;

    @BeforeClass
    public static void setup() throws IOException {
        fileContents = ... read TestFoo.class.getResourceAsStream("filename") ... 
    }   
}

Which works, but the problem I have is that I generally don't like non-final static data in my tests as it allows for the possibility of one test case to affect the output of another (in the above example if one test reassigned fileContents to something else, then there would be side-effects in other tests that make use of the fixture data).

If I add the final modifier though, then the assignment has to happen at declaration, which is a problem when the initialization is non-trivial (such as in the above example where a checked exception could be triggered by the initialization code). An alternative is to use a static initializer block:

public class TestFoo {

    private final static String fileContents;
    static {
        try {
            fileContents = ... read TestFoo.class.getResourceAsStream("filename") ...
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

But I don't particularly like this approach due to the verbosity of having to write a try/catch.

Which approach is more common/idiomatic Java, or is there a better/more typical way to have a static final resource initialized?

Adam Parkin
  • 14,883
  • 13
  • 57
  • 81
  • How could another test change the value of the field, since it's private? – JB Nizet Oct 31 '13 at 20:59
  • 1
    Why not use @Before annotation instead of @BeforeClass? – WeMakeSoftware Oct 31 '13 at 20:59
  • @JBNizet several test cases in one class? – WeMakeSoftware Oct 31 '13 at 21:00
  • You could have a method which loads the file as a String and throws an unchecked exception. – Peter Lawrey Oct 31 '13 at 21:00
  • @JBNizet: not another test suite, but two test cases within the ```TestFoo``` class. – Adam Parkin Oct 31 '13 at 21:02
  • @Funtik: because I don't want to pay the cost of reading a file on each test case, instead I'd like to read the file once. ```@Before``` gets triggered before each individual test case, and I/O is expensive. – Adam Parkin Oct 31 '13 at 21:03
  • Like how expensive your io is? Or is the file so big that you can't afford to do that? Premature optimization is the root of all evil – WeMakeSoftware Oct 31 '13 at 21:06
  • Encapsulation works mainly at the class level, because a class is supposed to trust itself. If another test method overwrites the value of the private static field, then it's a simple bug and it should be quite easy to find the culprit, since it's one of the other test methods of the same class. – JB Nizet Oct 31 '13 at 21:06
  • @JBNizet: careful, as it could lead to a sporadic bug depending upon the *order* in which each test method is run (if one method reassigns the value and that method is run last you won't see any error). – Adam Parkin Oct 31 '13 at 21:20
  • Frankly, I don't think this kind of bug has a high probability to happen, especially if you document the fact that the variable shouldn't be assigned. If you're really concerned about this bug to happen, then pay the small price of reading it before each test, or wrapping it in a try/catch block as you've done in your question. If you have to do it frequently, then write a reusable method that catches the IOException for you. – JB Nizet Oct 31 '13 at 21:24

1 Answers1

1

I'd use @BeforeClass because it's less verbose. If you fear that someone might accidentally overwrite the value, you could also use @Before. Unless the file is very large, the runtime overhead is probably negligible.

Peter Niederwieser
  • 111,903
  • 17
  • 295
  • 240
  • I should probably up it – WeMakeSoftware Oct 31 '13 at 21:12
  • The ```@Before``` is semi-valid: you need to remove the final qualifier to go that route, but that means while individual test cases are independent, there is still the possibility (though very unlikely) of within a single test case overwriting the value. And I/O can be prohibitive, not only if the file is not tiny, but also if you have a large number of test cases (with ```@Before``` you'd pay the "negligible" I/O cost for each test case -- including ones that don't use the read value). – Adam Parkin Oct 31 '13 at 21:19
  • It's a tradeoff between readability and safety, and personally I think the sweet spot is different for production code and tests. As for the performance, don't optimize prematurely. The files will be likely cached in memory, and if these are more than unit tests (which shouldn't use files anyway), it may not matter. And if you find it does, you can still revisit your decision. Ultimately, there is no single best solution here, and it's up to you to make a call. – Peter Niederwieser Oct 31 '13 at 23:03