Java – Advantage/Disadvantage of having all variables declared in a JUnit Test

javatddunit testing

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

  1. Each test is self contained.
  2. Each test can be executed in isolation, or in aggregation, with the same result.
  3. 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

  1. Increases code duplication
  2. 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 because bar is defined differently in that method).

Aside from convention, are there any other advantages/disadvantages of using either method over the other?

Best Answer

You should keep doing what you're doing.

Repeating yourself is just as bad an idea in test code as in business code, and for the same reason. Your colleague has been misled by the idea that every test should be self-contained. That's quite true, but "self-contained" doesn't mean that it should contain everything it needs within its method body. It only means that it should give the same result whether it's executed in isolaton or as part of a suite, and regardless of the order of the tests in the suite. In other words, the test code should have the same semantics regardless what other code has executed before it; it doesn't have to have all required code bundled textually.

Reusing constants, set-up code and the like increases the quality of the test code and doesn't compromise its self-contained-ness, so it's good thing that you should keep doing.

Related Topic