简体   繁体   English

如何获取 JUnit 测试套件类的注释?

[英]How to get annotations of JUnit test suite class?

I have such an example of test suite class to run many test classes at once in JUnit 4.13.我有这样一个测试套件类示例,可以在 JUnit 4.13 中一次运行多个测试类。

@RunWith(Suite.class)
@SuiteClasses({
    FirstTest.class,
    SecondTest.class
})
@TestSuiteAnnotation
public class TestSuite {
}

These are my test classes.这些是我的测试课。

@FirstAnnotation
public class FirstTest extends ExtTest {

  @Test
  public void test() {
  }

}

@SecondAnnotation
public class SecondTest extends ExtTest {

  @Test
  public void test() {
  }

}


public class ExtTest {

  @Before
  public void beforeMethod() {
    System.out.println("Annotations from " + this.getClass());
    Arrays.asList(this.getClass().getAnnotations()).forEach(System.out::println);
  }

}

When I run test from TestSuite.class , the console output is:当我从TestSuite.class运行测试时,控制台输出是:

Annotations from class FirstTest
@FirstAnnotation()

Annotations from class SecondTest
@SecondAnnotation()

Currently, this.getClass().getAnnotations() returns annotations from test classes (ie FirstTest.class , SecondTest.class ).目前, this.getClass().getAnnotations()从测试类(即FirstTest.classSecondTest.class )返回注解。 I want to obtain annotation @TestSuiteAnnotation , when I run tests from TestSuite.class .当我从TestSuite.class运行测试时,我想获得注释@TestSuiteAnnotation

The expected output should be:预期的输出应该是:

Annotations from class FirstTest
@FirstAnnotation()
@TestSuiteAnnotation()

Annotations from class SecondTest
@SecondAnnotation()
@TestSuiteAnnotation()

Can I somehow obtain annotation @TestSuiteAnnotation , when I run tests from TestSuite.class ?当我从TestSuite.class运行测试时,我可以以某种方式获得注释@TestSuiteAnnotation吗?

You have multiple options:您有多种选择:

JUnit 4 run listener JUnit 4 运行侦听器

On JUnit 4, you can register a RunListener , like @nrainer said.在 JUnit 4 上,您可以注册一个RunListener ,就像@nrainer所说的那样。 If you build with Maven, it is easy to register a run listener like this:如果您使用 Maven 构建,很容易注册一个运行侦听器,如下所示:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.22.2</version>
  <configuration>
    <includes>
      <include>org.acme.TestSuite</include>
    </includes>
    <properties>
      <property>
        <name>listener</name>
        <value>org.acme.SuiteRunListener</value>
      </property>
    </properties>
  </configuration>
</plugin>

The run listener can override the events testSuiteStarted and testSuiteFinished and either directly log the annotations you are interested in or assign them to a static thread-local variable like private static ThreadLocal<List<Annotation>> currentSuiteAnnotations in testSuiteStarted , then unassign it again in testSuiteFinished .运行侦听器可以覆盖事件testSuiteStartedtestSuiteFinished并直接记录您感兴趣的注释或将它们分配给静态线程局部变量,如testSuiteStarted private static ThreadLocal<List<Annotation>> currentSuiteAnnotations ,然后在testSuiteFinished再次testSuiteFinished分配.

This works nicely from Maven, I tested it.这在 Maven 中运行良好,我对其进行了测试。 Unfortunately, there is no direct support for running tests with run listeners from IDEs like IntelliJ IDEA or Eclipse.不幸的是,没有直接支持使用 IntelliJ IDEA 或 Eclipse 等 IDE 中的运行侦听器运行测试。 So if you want to avoid running the tests manually from a class with a main method as shown here , because it would take away all the nice IDE test reporting with drill-down from suite to test class to test method, this is not an option.所以,如果你想避免从main方法的类手动运行测试,显示在这里,因为它会带走所有的美好IDE测试报告包含详细的套件,测试类的测试方法,这是不是一种选择.

JUnit 5 test execution listener JUnit 5 测试执行监听器

Similar to JUnit 4's run listener, you can register a TestExecutionListener for your JUnit 5 tests.与 JUnit 4 的运行侦听器类似,您可以为 JUnit 5 测试注册一个TestExecutionListener The advantage in JUnit 5 is that you can register it globally via Java's s ServiceLoader mechanism, ie it will be picked up when bootstrapping JUnit and should also work in IDEs. JUnit 5 的优点是您可以通过 Java 的ServiceLoader机制全局注册它,即它会在引导 JUnit 时被拾取,并且也应该在 IDE 中工作。 I did something similar with another type of extension, and it worked nicely in IntelliJ IDEA and of course also in Maven.我用另一种类型的扩展做了类似的事情,它在 IntelliJ IDEA 中运行良好,当然在 Maven 中也运行良好。

JUnit 4 with custom suite runner带有自定义套件运行程序的 JUnit 4

Coming back to JUnit 4, we can extend the first approach with the run listener by declaring a special type of suite.回到 JUnit 4,我们可以通过声明一种特殊类型的套件来使用运行侦听器扩展第一种方法。 You simply use that suite instead of org.junit.runners.Suite and can enjoy the working run listener in both Maven and the IDE.您只需使用该套件而不是org.junit.runners.Suite ,就可以在 Maven 和 IDE 中享受正在运行的运行侦听器。 It works like that, see also my MCVE on GitHub for your convenience:它是这样工作的,为了您的方便,另请参阅我在 GitHub 上的 MCVE

package org.acme;

import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runner.notification.RunListener;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.stream.Collectors;

public class SuiteRunListener extends RunListener {
  private static ThreadLocal<String> currentSuiteName = new ThreadLocal<String>();
  private static ThreadLocal<List<Annotation>> currentSuiteAnnotations = new ThreadLocal<>();

  @Override
  public void testSuiteStarted(Description description) throws Exception {
    super.testSuiteStarted(description);
    final RunWith runWith = description.getAnnotation(RunWith.class);
    if (runWith != null && runWith.value().equals(SuiteWithListener.class)) {
      currentSuiteName.set(description.getDisplayName());
      currentSuiteAnnotations.set(
        description.getAnnotations().stream()
          .filter(annotation -> {
            final Class<? extends Annotation> annotationType = annotation.annotationType();
            return !(annotationType.equals(RunWith.class) || annotationType.equals(SuiteClasses.class));
          })
          .collect(Collectors.toList())
      );
    }
  }

  @Override
  public void testSuiteFinished(Description description) throws Exception {
    super.testSuiteFinished(description);
    final RunWith runWith = description.getAnnotation(RunWith.class);
    if (runWith != null && runWith.value().equals(SuiteWithListener.class)) {
      currentSuiteName.set(null);
      currentSuiteAnnotations.set(null);
    }
  }

  public static String getCurrentSuiteName() {
    return currentSuiteName.get();
  }

  public static List<Annotation> getCurrentSuiteAnnotations() {
    return currentSuiteAnnotations.get();
  }
}
package org.acme;

import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.Suite;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;

import java.util.List;

public class SuiteWithListener extends Suite {
  public SuiteWithListener(Class<?> klass, RunnerBuilder builder) throws InitializationError {
    super(klass, builder);
  }

  public SuiteWithListener(RunnerBuilder builder, Class<?>[] classes) throws InitializationError {
    super(builder, classes);
  }

  protected SuiteWithListener(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
    super(klass, suiteClasses);
  }

  protected SuiteWithListener(RunnerBuilder builder, Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
    super(builder, klass, suiteClasses);
  }

  protected SuiteWithListener(Class<?> klass, List<Runner> runners) throws InitializationError {
    super(klass, runners);
  }

  @Override
  public void run(RunNotifier notifier) {
    notifier.addListener(new SuiteRunListener());  // !!!
    super.run(notifier);
  }
}
package org.acme;

import org.junit.runner.RunWith;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(SuiteWithListener.class)  // !!!
@SuiteClasses({
  FirstTest.class,
  SecondTest.class
})
@TestSuiteAnnotation
public class TestSuite {}
package org.acme;

import org.junit.Before;

import java.util.Arrays;

public class ExtTest {
  @Before
  public void beforeMethod() {
    String currentSuiteName = SuiteRunListener.getCurrentSuiteName();
    if (currentSuiteName != null) {
      System.out.println("Annotations from suite " + currentSuiteName);
      SuiteRunListener.getCurrentSuiteAnnotations().forEach(System.out::println);
    }
    System.out.println("Annotations from class " + this.getClass());
    Arrays.asList(this.getClass().getAnnotations()).forEach(System.out::println);
    System.out.println();
  }
}

Now when running your suite, you should see output like this:现在,当运行您的套件时,您应该会看到如下输出:

Annotations from suite org.acme.TestSuite
@org.acme.TestSuiteAnnotation()
Annotations from class class org.acme.FirstTest
@org.acme.FirstAnnotation()

Annotations from suite org.acme.TestSuite
@org.acme.TestSuiteAnnotation()
Annotations from class class org.acme.SecondTest
@org.acme.SecondAnnotation()

Please note: I was assuming that you really need access to the current suite from each single test method, not just at the test class or suite level.请注意:我假设您确实需要从每个测试方法访问当前套件,而不仅仅是在测试类或套件级别。 If you do not need that and it is enough to let the run listener do something when a suite is started and/or finished, of course you do not need the getter methods for current suite name and suite annotations.如果您不需要它并且在套件启动和/或完成时让运行侦听器执行某些操作就足够了,当然您不需要当前套件名称和套件注释的 getter 方法。 I just extended your own example.我只是扩展了你自己的例子。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM