【问题标题】:JUnit4 skip test(s) according to custom java annotationsJUnit4 根据自定义 java 注释跳过测试
【发布时间】:2012-11-27 18:20:43
【问题描述】:

我希望根据我用 Java 创建的自定义注释来执行我的 JUnit4 测试。这个自定义注解的目的是让 JUnit4 注意到只有当机器的平台与注解中指定的平台匹配时才应该运行测试。

假设我有以下注释:

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

还有以下测试:

public class myTests{

    @BeforeClass
    public setUp() { ... }

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

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

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

}

如果我要在 Mac 机器上执行这些测试,那么只有 myTest1() 应该被执行,其余的应该被忽略。但是,我目前坚持如何实现这一点。如何让 JUnit 读取我的自定义注释并检查是否应该运行测试。

【问题讨论】:

标签: java junit annotations


【解决方案1】:

您可以使用类别,也可以实现自己的自定义 JUnit 运行器。扩展默认的 JUnit 运行器非常简单,并且允许您定义要以您可能想要的任何方式运行的测试列表。这包括仅查找具有特定注释的那些测试方法。我在下面包含了代码示例,您可以将它们用作您自己实现的基础:

注释:

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

自定义跑步者类:

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;
   }
}

示例测试类:

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

   @Test
   @MyCustomAnnotation(OS = "Mac")
   public void testCustomViaAnnotation() {
      return;
   }
}

【讨论】:

  • 我打算用自定义跑步者发布一个答案,但看到你提到它,介意我是否扩展你的答案吗?
  • @Perception:请这样做,我想对此有更深入的了解。
  • @AlexR:不确定您所说的“类别”是什么意思,请您提供更多详细信息。抱歉,还是 JUnit 和 Java 的新手。提前谢谢你!
  • 我添加了一些代码来说明如何创建自定义 JUnit 运行器。 +1 @AlexR。
  • 这并不理想! computeTestMethods 实际上在调用子类构造函数之前由祖先的 (ParentRunner) 构造函数通过validate() 调用,因此如果此方法中有任何内容或任何覆盖版本的利用实例变量很可能会导致错误/空异常。应该覆盖getChildren 来过滤测试方法。
【解决方案2】:

我发现有这种行为并将它们作为报告中的意识跳过测试的最佳方法是使用您自己的跑步者(如在 AlexR 的答案中)但覆盖 runChild 方法,该方法允许选择测试但像忽略而不是完全排除一样处理。

要使用的注解

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

    String name() default "";

    String arch() default "";

    String version() default "";
}

JUnit 跑步者

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);
        }
    }
}

在测试中的使用

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

并限制特定的方法

@Test
@TargetOS(family = "windows")
public void testGetFileFromUrlInternetExplorer() throws Exception {
    ...
}

【讨论】:

    【解决方案3】:

    到目前为止,我发现的最佳选择是通过JUnit Rule。有一个link to GitHub 有一个可以使用的文件

    【讨论】:

      【解决方案4】:

      这正是 JUnit 类别(参见 short introduction)的用途。

      用适当的类别标记所有测试后(使用@Category),您可以创建运行所有测试但类别错误或所有测试具有正确类别的套件。 (使用@IncludeCategory 和@ExcludeCategory,您可以将它们组合起来以缩小您的选择范围)

      类别可用于套件、测试类甚至测试方法级别。

      这里有更多关于JUnit Categories的信息

      【讨论】:

        【解决方案5】:

        您可以使用此注解启用和禁用测试方法@DisabledOnOs(OS.LINUX)@EnabledOnOs({OS.WINDOWS, OS.MAC})

        【讨论】:

          猜你喜欢
          • 2017-07-19
          • 1970-01-01
          • 2020-10-08
          • 1970-01-01
          • 2021-12-01
          • 2016-10-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多