JUnit5-Jupiter:组合(=“元")注释无法解析为注释定义 [英] JUnit5-Jupiter: Composed (="meta") annotation does not resolve to annotation definition

查看:36
本文介绍了JUnit5-Jupiter:组合(=“元")注释无法解析为注释定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我定义了自己的 JUnit 注释:

@ParameterizedTest@MethodSource("myorg.qa.ccrtesting.DataProviders#standardDataProvider")@Tags({@Tag("ccr"), @Tag("standard")})公共@interface CcrStandardTest {}

然后,我可以在测试中使用该注释:

@CcrStandardTestpublic void E0010_contact_standard (String testData) {...

  • 我的运行配置:
    JVM 选项:-ea
    类:myorg.qa.ccrtesting.ccrstandardtests.CcrStanConTest - 这是 IDE 建议的(经过验证指向正确的类,该类包含我的原型测试方法)

然而,这会导致:jupiter.api.extension.ParameterResolutionException:没有为方法 [public void...

中的参数 [java.lang.String arg0] 注册 ParameterResolver
  • 我尝试从测试方法签名中删除 String testData 但随后 JUnit 没有执行任何测试:No tests found

  • 当我在原型测试方法上方添加 @Test 时,它会执行:

    1. 我在 @CcrStandardTest 下定义的注释似乎都没有应用
    2. IDE 建议 @Test 和参数化源的可疑组合
      (我已经知道 @ParameterizedTest 意味着 @Test,只是不知道为什么 IDE 能够找到自定义注释而 JUnit 不能?)

解决方案

如您所见,您需要添加 @Retention(RUNTIME) 到您的组合注释,以便 JUnit看见.Java 中的注释具有三种不同的保留策略:

  • RetentionPolicy.SOURCE

    <块引用>

    注释将被编译器丢弃.

  • RetentionPolicy.CLASS

    <块引用>

    注释将由编译器记录在类文件中,但在运行时不需要由 VM 保留.这是默认行为. [强调]

  • RetentionPolicy.RUNTIME

    <块引用>

    注解会被编译器记录在类文件中,并在运行时由虚拟机保留,因此可以反射读取.

正如我上面强调的,如果您没有明确添加 @Retention(...),则使用 CLASS 策略.这不适用于 JUnit,因为 JUnit 不会扫描 *.class 文件(即字节码)以获取注释,它会反射性地扫描 加载的 类以查找测试方法.如果没有 RUNTIME 保留策略,您的注释将无法反射访问,因此 JUnit 永远不会看到它,因此不会执行测试.

@Target 注释:

<块引用>

指示注释类型适用的上下文.注释类型可能适用的声明上下文和类型上下文在 JLS 9.6.4.1 中指定,并在源代码中由 java.lang.annotation.ElementType.>

如果 @Target 元注解不存在于注解类型 T 上,那么 T 类型的注解可以写成除类型参数声明外的任何声明的修饰符.

如果存在 @Target 元注释,编译器将强制执行 ElementType 枚举常量指示的使用限制,符合 JLS 9.7.4.

在我对你的其他问题的回答中,我使用了:

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})

因为这与 @ParameterizedTest.我认为将其限制为 METHOD 是个好主意,因为 @ParameterizedTest 的设计者显然认为只有方法应该由参数化测试扩展直接扩展(参见 §5 扩展模型).并且包含 ANNOTATION_TYPE 允许您将组合注释放在另一个注释上,从而创建另一个组合注释.

您还会看到我包含了 @Documented:

<块引用>

如果注解 @Documented 出现在注解类型 A 的声明中,则任何 @A> 元素上的注释被视为元素公共契约的一部分.更详细地说,当注解类型 ADocumented 进行注解时,A 类型的注解的存在和值A 注释元素的公共契约的一部分.相反,如果注释类型 B 没有用 Documented 注释,则 B 的存在和值 注释不是 B 注释元素的公共契约的一部分.具体来说,如果一个注解类型用 Documented 进行注解,默认情况下,像 javadoc 这样的工具将在其输出中显示该类型的注解,而没有 Documented 的注解类型的注解不会被显示.

请注意,这些注释(@Retention@Target@Documented)并非特定于 JUnit.这些注释是 Java 中注释如何工作的基础,每个注释都驻留在 java.lang.annotation 包中.

I defined my own JUnit annotation:

@ParameterizedTest
@MethodSource("myorg.qa.ccrtesting.DataProviders#standardDataProvider")
@Tags({@Tag("ccr"), @Tag("standard")})
public @interface CcrStandardTest {
}

Then, I was able to use that annotation in my tests:

@CcrStandardTest
public void E0010_contact_standard (String testData) {
...

  • My run configuration:
    JVM options: -ea
    Class: myorg.qa.ccrtesting.ccrstandardtests.CcrStanConTest - This was suggested by the IDE (and is verified to point to the correct class, which holds my prototype test method)

However, this results in: jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [java.lang.String arg0] in method [public void...

  • I tried removing String testData from the test method signature but then JUnit is not executing any tests: No tests found

  • When I add @Test above my prototype test method, it executes but:

    1. It seems like none of the annotations I defined under @CcrStandardTest are applied
    2. IDE suggests suspicious combination @Test and parameterized source
      (I already know @ParameterizedTest implies @Test, just not sure why IDE is able to find the custom annotation but JUnit isn't?)

解决方案

As you've discovered, you need to add @Retention(RUNTIME) to your composed annotation in order for JUnit to see it. Annotations in Java have three different retention policies:

  • RetentionPolicy.SOURCE

    Annotations are to be discarded by the compiler.

  • RetentionPolicy.CLASS

    Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time. This is the default behavior. [emphasis added]

  • RetentionPolicy.RUNTIME

    Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.

As I emphasized above, if you don't explicitly add @Retention(...) then the CLASS policy is used. This won't work with JUnit because JUnit doesn't scan the *.class files (i.e. byte-code) for the annotations, it scans the loaded classes reflectively to find test methods. Without a RUNTIME retention policy your annotation is not reflectively accessible, thus JUnit never sees it and consequently doesn't execute the test.

The @Target annotation:

Indicates the contexts in which an annotation type is applicable. The declaration contexts and type contexts in which an annotation type may be applicable are specified in JLS 9.6.4.1, and denoted in source code by enum constants of java.lang.annotation.ElementType.

If an @Target meta-annotation is not present on an annotation type T , then an annotation of type T may be written as a modifier for any declaration except a type parameter declaration.

If an @Target meta-annotation is present, the compiler will enforce the usage restrictions indicated by ElementType enum constants, in line with JLS 9.7.4.

In my answer to your other question I used:

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})

Because that's the same targets used by @ParameterizedTest. I figured it was a good idea to restrict it to METHOD since the designers of @ParameterizedTest apparently feel that only methods should be directly extended by the parameterized-tests extension (see §5 Extension Model). And including ANNOTATION_TYPE allows you to place your composed annotation on another annotation, creating yet another composed annotation.

You'll also see I included @Documented:

If the annotation @Documented is present on the declaration of an annotation type A, then any @A annotation on an element is considered part of the element's public contract. In more detail, when an annotation type A is annotated with Documented, the presence and value of annotations of type A are a part of the public contract of the elements A annotates. Conversely, if an annotation type B is not annotated with Documented, the presence and value of B annotations are not part of the public contract of the elements B annotates. Concretely, if an annotation type is annotated with Documented, by default a tool like javadoc will display annotations of that type in its output while annotations of annotation types without Documented will not be displayed.

Notice that theses annotations—@Retention, @Target, and @Documented—are not specific to JUnit. These annotations are fundamental to how annotations in Java work and each one resides in the java.lang.annotation package.

这篇关于JUnit5-Jupiter:组合(=“元")注释无法解析为注释定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆