Best Practice: Initialize JUnit class fields in setUp() or at declaration?
Should I initialize class fields at declaration like this?
public class SomeTest extends TestCase
{
private final List list = new ArrayList();
public void testPopulateList()
{
// Add stuff to the list
// Assert the list contains what I expect
}
}
Or in setUp() like this?
public class SomeTest extends TestCase
{
private List list;
@Override
protected void setUp() throws Exception
{
super.setUp();
this.list = new ArrayList();
}
public void testPopulateList()
{
// Add stuff to the list
// Assert the list contains what I expect
}
}
I tend to use the first form because it's more concise, and allows me to use final fields. If I don't need to use the setUp() method for set-up, should I still use it, and why?
Clarification:
JUnit will instantiate the test class once per test method. That means list
will be created once per test, regardless of where I declare it. It also means there are no temporal dependencies between the tests. So it seems like there are no advantages to using setUp(). However the JUnit FAQ has many examples that initialize an empty collection in setUp(), so I figure there must be a reason.
Solution 1:
If you're wondering specifically about the examples in the JUnit FAQ, such as the basic test template, I think the best practice being shown off there is that the class under test should be instantiated in your setUp method (or in a test method).
When the JUnit examples create an ArrayList in the setUp method, they all go on to test the behavior of that ArrayList, with cases like testIndexOutOfBoundException, testEmptyCollection, and the like. The perspective there is of someone writing a class and making sure it works right.
You should probably do the same when testing your own classes: create your object in setUp or in a test method, so that you'll be able to get reasonable output if you break it later.
On the other hand, if you use a Java collection class (or other library class, for that matter) in your test code, it's probably not because you want to test it--it's just part of the test fixture. In this case, you can safely assume it works as intended, so initializing it in the declaration won't be a problem.
For what it's worth, I work on a reasonably large, several-year-old, TDD-developed code base. We habitually initialize things in their declarations in test code, and in the year and a half that I've been on this project, it has never caused a problem. So there's at least some anecdotal evidence that it's a reasonable thing to do.
Solution 2:
I started digging myself and I found one potential advantage of using setUp()
. If any exceptions are thrown during the execution of setUp()
, JUnit will print a very helpful stack trace. On the other hand, if an exception is thrown during object construction, the error message simply says JUnit was unable to instantiate the test case and you don't see the line number where the failure occurred, probably because JUnit uses reflection to instantiate the test classes.
None of this applies to the example of creating an empty collection, since that will never throw, but it is an advantage of the setUp()
method.
Solution 3:
In addition to Alex B's answer.
It is even required to use the setUp method to instantiate resources in a certain state. Doing this in the constructor is not only a matter of timings, but because of the way JUnit runs the tests, each test state would be erased after running one.
JUnit first creates instances of the testClass for each test method and starts running the tests after each instance is created. Before running the test method, its setup method is ran, in which some state can be prepared.
If the database state would be created in the constructor, all instances would instantiate the db state right after each other, before running each tests. As of the second test, tests would run with a dirty state.
JUnits lifecycle:
- Create a different testclass instance for each test method
- Repeat for each testclass instance: call setup + call the testmethod
With some loggings in a test with two test methods you get: (number is the hashcode)
- Creating new instance: 5718203
- Creating new instance: 5947506
- Setup: 5718203
- TestOne: 5718203
- Setup: 5947506
- TestTwo: 5947506
Solution 4:
In JUnit 4:
- For the Class Under Test, initialize in a
@Before
method, to catch failures. - For other classes, initialize in the declaration...
- ...for brevity, and to mark fields
final
, exactly as stated in the question, - ...unless it is complex initialization that could fail, in which case use
@Before
, to catch failures.
- ...for brevity, and to mark fields
- For global state (esp. slow initialization, like a database), use
@BeforeClass
, but be careful of dependencies between tests. - Initialization of an object used in a single test should of course be done in the test method itself.
Initializing in a @Before
method or test method allows you to get better error reporting on failures. This is especially useful for instantiating the Class Under Test (which you might break), but is also useful for calling external systems, like filesystem access ("file not found") or connecting to a database ("connection refused").
It is acceptable to have a simple standard and always use @Before
(clear errors but verbose) or always initialize in declaration (concise but gives confusing errors), since complex coding rules are hard to follow, and this isn't a big deal.
Initializing in setUp
is a relic of JUnit 3, where all test instances were initialized eagerly, which causes problems (speed, memory, resource exhaustion) if you do expensive initialization. Thus best practice was to do expensive initialization in setUp
, which was only run when the test was executed. This no longer applies, so it is much less necessary to use setUp
.
This summarizes several other replies that bury the lede, notably by Craig P. Motlin (question itself and self-answer), Moss Collum (class under test), and dsaff.