Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JUnit4 skip test(s) according to custom java annotations

I would like my JUnit4 Tests to be executed according to a custom annotation that I created with Java. The purpose of this custom annotation is for JUnit4 to note that the test should only be run if the machine's platform matches the one specified in the annotation.

Say I have the following annotation:

public @interface Annotations {
    String OS();
    ...
}

And the following Tests:

public class myTests{

    @BeforeClass
    public setUp() { ... }

    @Annotations(OS="mac")
    @Test
    public myTest1() { ... }

    @Annotations(OS="windows")
    @Test
    public myTest2() { ... }

    @Annotation(OS="unix")
    @Test
    public myTest3() { ... }

}

If I were to execute these tests in a Mac machine, then only myTest1() should be executed and the rest should be ignored. However, I am currently stuck on how I should implement this. How do I let JUnit read my custom annotation and check whether the test should be run or not.

like image 249
user1754960 Avatar asked Nov 27 '12 18:11

user1754960


People also ask

How do you skip a test case in Java?

If you want to ignore a test method, use @Ignore along with @Test annotation. If you want to ignore all the tests of class, use @Ignore annotation at the class level.

Is junit5 better than junit4?

Only one test runner can execute tests at a time in JUnit 4 (e.g. SpringJUnit4ClassRunner or Parameterized ). JUnit 5 allows multiple runners to work simultaneously. JUnit 4 never advanced beyond Java 7, missing out on a lot of features from Java 8. JUnit 5 makes good use of the Java 8 features.

Can you ignore a test method?

Apart from disabling a single method, you can also disable all methods of a class by using @Ignore annotation at the class level. For example, If you annotate a class containing test methods with @Ignore and none of the containing tests will be executed.


3 Answers

The best way I found on having exactly this behavior and having them as skipped tests for awareness in the report is using your own runner (like in the answer of AlexR) but overriding the runChild method which allows the test to be picked but handled like an ignore and not completely excluded.

The annotation to be used

@Retention(RetentionPolicy.RUNTIME)
public @interface TargetOS {
    String family();

    String name() default "";

    String arch() default "";

    String version() default "";
}

The JUnit runner

public class OSSensitiveRunner extends BlockJUnit4ClassRunner {
    public OSSensitiveRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
        Description description = describeChild(method);
        if (method.getAnnotation(Ignore.class) != null) {
            notifier.fireTestIgnored(description);
        } else if (method.getAnnotation(TargetOS.class) != null) {
            final TargetOS tos = method.getAnnotation(TargetOS.class);
            String name = tos.name().equals("") ? null : tos.name();
            String arch = tos.arch().equals("") ? null : tos.arch();
            String version = tos.version().equals("") ? null : tos.version();
            if (OS.isOs(tos.family(), name, arch, version)) {
                runLeaf(methodBlock(method), description, notifier);
            } else {
                notifier.fireTestIgnored(description);
            }
        } else {
            runLeaf(methodBlock(method), description, notifier);
        }
    }
}

Usage in a test

@RunWith(OSSensitiveRunner.class)
public class SeleniumDownloadHelperTest {
...

And restricting a specific method

@Test
@TargetOS(family = "windows")
public void testGetFileFromUrlInternetExplorer() throws Exception {
    ...
}
like image 156
rac2030 Avatar answered Oct 01 '22 12:10

rac2030


You can either use categories, or you can implement your own custom JUnit runner. Extending the default JUnit runner is pretty straightforward, and allows you to define the list of tests to be run in any way you might want. This includes looking for only those test methods with a specific annotation. I am including code samples below, you can use them as a basis for your own implementation:

Annotation:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation {
   String OS();
}

Custom Runner Class:

public class MyCustomTestRunner extends BlockJUnit4ClassRunner {

   public MyCustomTestRunner(final Class<?> klass) throws InitializationError {
      super(klass);
   }

   @Override
   protected List<FrameworkMethod> computeTestMethods() {
      // First, get the base list of tests
      final List<FrameworkMethod> allMethods = getTestClass()
            .getAnnotatedMethods(Test.class);
      if (allMethods == null || allMethods.size() == 0)
         return allMethods;

      // Filter the list down
      final List<FrameworkMethod> filteredMethods = new ArrayList<FrameworkMethod>(
            allMethods.size());
      for (final FrameworkMethod method : allMethods) {
         final MyCustomAnnotation customAnnotation = method
               .getAnnotation(MyCustomAnnotation.class);
         if (customAnnotation != null) {
            // Add to accepted test methods, if matching criteria met
            // For example `if(currentOs.equals(customAnnotation.OS()))`
            filteredMethods.add(method);
         } else {
            // If test method doesnt have the custom annotation, either add it to
            // the accepted methods, or not, depending on what the 'default' behavior
            // should be
            filteredMethods.add(method);
         }
      }

      return filteredMethods;
   }
}

Sample Test Class:

@RunWith(MyCustomTestRunner.class)
public class MyCustomTest {
   public MyCustomTest() {
      super();
   }

   @Test
   @MyCustomAnnotation(OS = "Mac")
   public void testCustomViaAnnotation() {
      return;
   }
}
like image 34
AlexR Avatar answered Oct 01 '22 14:10

AlexR


This is exactly what JUnit categories (see this short introduction) are made for.

After you marked all the tests with the appropriate category (using @Category), you can than create suites that run all the tests but those of the wrong category or all the tests that have the right category. (using the @IncludeCategory and @ExcludeCategory, you can combine them to narrow down your selection)

Categories can be used on Suite, Test-class and even Test-Method level.

Here are more informations regarding JUnit Categories

like image 28
KFleischer Avatar answered Oct 01 '22 14:10

KFleischer