1. Overview

In this tutorial, we'll examine how we can create a custom JUnit test rule to introduce new behavior to our tests. In the end, we'll have a test rule which ignores tests according to some given condition.

2. Create Custom Ignore Test Rule

Let's create a basic test rule which allows us to ignore tests by using an annotation.

Firstly, we'll create the @CustomIgnore annotation to mark the tests:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
public @interface CustomIgnore {

}

Our @CustomIgnore annotation doesn't include any attribute since we'll use it only for marking.

Then, we'll write our CustomIgnoreRule class which is an implementation of TestRule. It'll look for the @CustomIgnore annotation on tests. If the annotation is present, the test will continue to run. Otherwise, it'll be skipped:

public class CustomIgnoreRule implements TestRule {

    @Override
    public Statement apply(Statement base, Description description) {
        return new IgnorableStatement(base, description);
    }

    private class IgnorableStatement extends Statement {

        private final Statement base;

        private final Description description;

        public IgnorableStatement(Statement base, Description description) {
            this.base = base;
            this.description = description;
        }

        @Override
        public void evaluate() throws Throwable {
            CustomIgnore annotation = description.getAnnotation(CustomIgnore.class);
            boolean shouldIgnore = annotation != null;
            Assume.assumeTrue("Test is ignored!", !shouldIgnore);
            base.evaluate();
        }
    }
}

Note that we're using the Description instance to get the CustomIgnore annotation. Then we're invoking Assume.assumeTrue to control the test execution.

Lastly, we'll apply the rule to our test class:

public class CustomIgnoreTest {

    @Rule
    public CustomIgnoreRule customIgnoreRule = new CustomIgnoreRule();

    @CustomIgnore
    @Test
    public void shouldIgnore() {
        fail("Should have been ignored!");
    }

    @Test
    public void shouldNotIgnore() {
        System.out.println("Hello World!");
    }
}

3. Create Conditional Ignore Test Rule

Now let's enhance our implementation and make it ignore tests conditionally.

@ConditionalIgnore is the annotation to mark tests and define conditions. In our case, conditions will depend on key/value pairs.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
public @interface ConditionalIgnore {

    String key() default "";

    String value() default "";
}

Here, we're defining two attributes, key and value. These values will be populated per test method.

Next, we have the ConditionalIgnoreRule class similar to CustomIgnoreRule.

It'll first look for the @ConditionalIgnore annotation on the test method. Then it'll search the given key in the System properties and will compare it with the given value. If the values match, the test will continue to execute. Otherwise, it'll skip it.

public class ConditionalIgnoreRule implements TestRule {

    @Override
    public Statement apply(Statement base, Description description) {
        return new IgnorableStatement(base, description);
    }

    private class IgnorableStatement extends Statement {

        private final Statement base;

        private final Description description;

        public IgnorableStatement(Statement base, Description description) {
            this.base = base;
            this.description = description;
        }

        @Override
        public void evaluate() throws Throwable {
            boolean shouldIgnore = false;
            ConditionalIgnore annotation = description.getAnnotation(ConditionalIgnore.class);
            if (annotation != null) {
                String key = annotation.key();
                String value = annotation.value();
                String actualValue = System.getProperty(key);
                if (!StringUtils.equalsIgnoreCase(value, actualValue)) {
                    shouldIgnore = true;
                }
            }

            Assume.assumeTrue("Test is ignored!", !shouldIgnore);
            base.evaluate();
        }
    }
}

Lastly, we'll apply our TestRule to a test class:

public class ConditionalIgnoreTest {

    @Rule
    public ConditionalIgnoreRule conditionalIgnoreRule = new ConditionalIgnoreRule();

    @ConditionalIgnore(key = "line.separator", value = "\n")
    @Test
    public void shouldRunIfType1() {
        System.out.println("Type 1");
    }

    @ConditionalIgnore(key = "type", value = "type2")
    @Test
    public void shouldRunIfType2() {
        System.out.println("Type 2");
    }
}

4. Summary

In this tutorial, we've implemented custom JUnit test rules which lets us ignore some tests. These examples can serve as a starting point for other similar implementations.

As always, the source code is available on Github.