Friday, March 02, 2012

Showing test failure name for JUnit parameterized tests

A popular complaint against JUnit is lack of human-readable names for parameterized test cases. There are several solutions, this is mine.

/**
 * {@code ParameterizedTest} simplifies parametered JUnit tests by including a "case label" to
 * identify the failing branch.  (JUnit only provides an index).  The failure message format
 * becomes "descriptive string from JUnit: case label: assertion failure message".
 * <p/>
 * Fully preserves the stack trace of failing tests.
 * <p/>
 * Example: <pre>
 * public class MyTest {
 *     &#64;Parameters
 *     public static Collection<Object[]> parameters() {
 *         return ...; // See JUnit documentation for parameterized tests
 *     }
 *
 *     public MyTest(final String caseLabel, final X x, final Y y) {
 *         super(caseLabel);
 *         this.x = x;
 *         this.y = y;
 *     }
 *
 *     &#64;Test
 *     public void shouldWork() {
 *         // Body of test method ...
 *     }
 * }
 * </pre>
 * When {@code shouldWork()} fails the failure message is more meaningful, including the
 * {@link #caseLabel "case label"} in the JUnit error message.
 *
 * @author <a href="mailto:binkley@alumni.rice.edu">B. K. Oxley (binkley)</a>
 */
@RunWith(Parameterized.class)
public abstract class ParameterizedTest {
    private final String caseLabel;

    /**
     * The JUnit test rule (test extension) which fails bad parameterized cases but provides a
     * {@link #caseLabel case label} identifing the failed test branch.
     */
    @Rule
    public final TestRule parameterizedTestRule = new TestWatcher() {
        @Override
        protected void failed(final Throwable e, final Description description) {
            final AssertionError x = new AssertionError(
                    description.getDisplayName() + ": " + caseLabel + ": " + e.getMessage());
            x.setStackTrace(e.getStackTrace());
            throw x;
        }
    };

    /**
     * Constructs a new {@code ParameterizedTest} for the given <var>caseLabel</var>.
     *
     * @param caseLabel the case label, never missing
     */
    protected ParameterizedTest(@Nonnull final String caseLabel) {
        this.caseLabel = caseLabel;
    }
}

1 comment:

Victor said...

Thanks, this was a good workaround.
I've just read that support for parameter names was added last week with JUnit 4.11:
https://github.com/KentBeck/junit/blob/master/doc/ReleaseNotes4.11.md