Sunday, March 31, 2013

JUnit: Naming Individual Test Cases in a Parameterized Test

A couple of years ago I wrote about JUnit Parameterized Tests. One of the things I didn't like about them was that JUnit named the invidividual test cases using numbers, so if they failed you had no idea which test parameters caused the failure. The following Eclipse screenshot will show you what I mean:

A parameterised test without names

However, in JUnit 4.11, the @Parameters annotation now takes a name argument which can be used to display the parameters in the test name and hence, make them more descriptive. You can use the following placeholders in this argument and they will be replaced by actual values at runtime by JUnit:

  • {index}: the current parameter index
  • {0}, {1}, ...: the first, second, and so on, parameter value
Here is an example:

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class StringSortTest {

  @Parameters(name = "{index}: sort[{0}]={1}")
  public static Collection<Object[]> data() {
    return Arrays.asList(new Object[][] {
          { "abc", "abc"},
          { "cba", "abc"},
          { "abcddcba", "aabbccdd"},
          { "a", "a"},
          { "aaa", "aaa"},
          { "", ""}
        });
  }

  private final String input;
  private final String expected;

  public StringSortTest(final String input, final String expected){
    this.input = input;
    this.expected = expected;
  }

  @Test
  public void testSort(){
    assertEquals(expected, sort(input));
  }

  private static String sort(final String s) {
    final char[] charArray = s.toCharArray();
    Arrays.sort(charArray);
    return new String(charArray);
  }
}
When you run the test, you will see individual test cases named as shown in the Eclipse screenshot below, so it is easy to identify the parameters used in each test case.

A parameterised test with individual test case naming

Note that due to a bug in Eclipse, names containing brackets are truncated. That's why I had to use sort[{0}], instead of sort({0}).

5 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. I was going through the javadoc of @RunWith annotation and I see that it is not mature enough. The following is what I see in the javadoc of RunWith annotation.
    Kind of warning to developers??

    When a class is annotated with @RunWith or extends a class annotated with @RunWith, JUnit will invoke the class it references to run the tests in that class instead of the runner built into JUnit. We added this feature late in development. While it seems powerful we expect the runner API to change as we learn how people really use it. Some of the classes that are currently internal will likely be refined and become public. For example, suites in JUnit 4 are built using RunWith, and a custom runner named Suite:
    @RunWith(Suite.class)
    @SuiteClasses({ATest.class, BTest.class, CTest.class})
    public class ABCSuite {
    }
    I had written a blog post on Junit Test cases categories using annotation which like @RunWith is also in its infancy.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I have also implemented a JUnit plugin to provide better parameterized tests to JUnit (similar to TestNG). As the internal structure of TestNG is a bit different from JUnit, it’s not completely equal but IMHO it provides the most important features for parameterized testing.

    See https://github.com/TNG/junit-dataprovider

    Feedback welcome :-)

    ReplyDelete