TestNG. Skip tests on condition

Sometimes you need to disable/skip some of your TestNG tests based on condition. You cannot use @Test(enabled = false) because you need to check the condition runtime.

Of course you can check the condition in your test just like this

@Test
public void conditionalTest() {
  if (EnvironmentUtils.isProduction()) {
    throw new SkipException("These Tests shouldn't be run in Production");
  }

  // Your test code
}

Doesn’t look good, right? What if we could skip test based on a condition like this using annotations

@NonProduction
@Test
public void conditionalTest() {
  // Your test code
}

Looks much more elegant, doesn’t it?

Using IInvokedMethodListener for conditional skipping TestNG tests

We can use that approach if we implement TestNG’s IInvokedMethodListener. Here is how to do that.

package io.lenar.examples.conditional;

import java.lang.reflect.Method;

import org.testng.IInvokedMethod;
import org.testng.IInvokedMethodListener;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.internal.ConstructorOrMethod;

public class ConditionalSkipTestAnalyzer implements IInvokedMethodListener {
    public void beforeInvocation(IInvokedMethod invokedMethod, ITestResult result) {
        Method method = result.getMethod().getConstructorOrMethod().getMethod();
        if (method == null) {
            return;
        }
        if (method.isAnnotationPresent(NonProduction.class) 
              && EnvironmentUtils.isProduction()) {
            throw new SkipException("These Tests shouldn't be run in Production");
        }
        if (method.isAnnotationPresent(ProductionOnly.class) 
              && !EnvironmentUtils.isProduction()) {
            throw new SkipException("These Tests should be run in Production only");
        }
        if (method.isAnnotationPresent(HappyFriday.class) 
              && TimeAndDatesUtils.isFridayToday()) {
            throw new SkipException("These Tests shouldn't be run on Friday");
        }
        if (method.isAnnotationPresent(HateRain.class) 
              && WheatherUtils.isItRaining()) {
            throw new SkipException("These Tests shouldn't be run in case of rain");
        }
        return;
    }

    public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
    }
}

You can use as many conditions for skipping TestNG tests as you need, just create annotations for each condition and check those conditions in IInvokedMethodListener’s beforeInvocation method.

Annotations for conditional skipping tests

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ProductionOnly {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ProductionOnly {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ProductionOnly {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ProductionOnly {
}

Using annotations for conditional skipping tests

Here is an example how to use annotations for conditional skipping TestNG tests. You can even combine conditions - see test5()

@Listeners(value = ConditionalSkipTestAnalyzer.class)
public class ExampleConditionalSkippingTest {

    @NonProduction
    @Test
    public void test1() {
        // test code
    }

    @ProductionOnly
    @Test
    public void test2() {
        // test code
    }

    @HappyFriday
    @Test
    public void test3() {
        // test code
    }

    @HateRain
    @Test
    public void test4() {
        // test code
    }

    @ProductionOnly
    @HappyFriday
    @Test
    public void test5() {
        // test code
    }

}

Compatible with Spring Boot

That approach for conditional skipping tests also works if you use TestNG and Spring Boot together

@SpringBootTest(classes = ExampleTestContext.class)
@Listeners(value = ConditionalSkipTestAnalyzer.class)
public class ExampleConditionalSkippingTest extends AbstractTestNGSpringContextTests {
  // test code
  ...
}

You may also find these posts interesting: