I've been writing some unit tests for some new code at work, and sent it off for a code review. One of my co-workers made a comment about why I was putting variables that are used in a number of those tests outside of the scope for the test.
The code I posted was essentially
import org.junit.Test;
public class FooUnitTests {
@Test
public void testConstructorWithValidName() {
new Foo(VALID_NAME);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorWithNullName() {
new Foo(null);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorWithZeroLengthName() {
new Foo("");
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorWithLeadingAndTrailingWhitespaceInName() {
final String name = " " + VALID_NAME + " ";
final Foo foo = new Foo(name);
assertThat(foo.getName(), is(equalTo(VALID_NAME)));
}
private static final String VALID_NAME = "name";
}
His proposed changes were essentially
import org.junit.Test;
public class FooUnitTests {
@Test
public void testConstructorWithValidName() {
final String name = "name";
final Foo foo = new Foo(name);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorWithNullName() {
final String name = null;
final Foo foo = new Foo(name);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorWithZeroLengthName() {
final String name = "";
final Foo foo = new Foo(name);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorWithLeadingAndTrailingWhitespaceInName() {
final String name = " name ";
final Foo foo = new Foo(name);
final String actual = foo.getName();
final String expected = "name";
assertThat(actual, is(equalTo(expected)));
}
}
Where everything that is required within the scope of the test, is defined within the scope of the test.
Some of the advantages he argued were
- Each test is self contained.
- Each test can be executed in isolation, or in aggregation, with the same result.
- Reviewer doesn't have to scroll to wherever these parameters are declared to look up what the value is.
Some of the disadvantages of his method that I argued were
- Increases code duplication
- Can add noise to the reviewers mind if there are several similar tests with different values defined (ie. test with
doFoo(bar)
results in one value, while the same call has a different result becausebar
is defined differently in that method).
Aside from convention, are there any other advantages/disadvantages of using either method over the other?