(Using OpenJDK-13 and JUnit5-Jupiter)
The problem is that my unit tests each make use of a not-small JUnit annotation system, something like this:
#ParameterizedTest
#MethodSource("myorg.ccrtest.testlogic.DataProviders#standardDataProvider")
#Tags({#Tag("ccr"), #Tag("standard")})
This makes test authoring a little tedious, test code a little long and of course, when a change is needed, it's a chore!
Was wondering if I could create my own JUnit annotation: #CcrStandardTest, which would imply all of the annotations above?
I also tried shifting the annotations up in the class definition (hoping they would then apply to all methods of the class), but the compiler says no: "#ParameterizedTest is not applicable to type"
You can make a composed annotation:
JUnit Jupiter annotations can be used as meta-annotations. That means that you can define your own composed annotation that will automatically inherit the semantics of its meta-annotations.
For example:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
#ParameterizedTest
#MethodSource("myorg.ccrtest.testlogic.DataProviders#standardDataProvider")
#Tag("ccr")
#Tag("standard")
public #interface CcrStandardTest {}
You would then place the composed annotation on your test method:
#CcrStandardTest
void testFoo(/* necessary parameters */) {
// perform test
}
Related
In JUnit 5 you can define your own test template and bind it to an annotation.
Instead of #Test or #ParameterizedTest now I can use my own test annotaion #MyTest(...)
The problem is that Intellij does not recognize this annotation as JUnit test annotation thus does not give me the option to run/debug this test.
Can I extend this list of annotations that tells IntelliJ this is a test?
Picture on the left is a standard test where the context shows a run function.
Picture on the right shows my template which works perfectly just the context does not recognize it as a test.
My test template looks like this:
#Target(
AnnotationTarget.ANNOTATION_CLASS,
AnnotationTarget.FUNCTION
)
#Retention(AnnotationRetention.RUNTIME)
#Execution(ExecutionMode.CONCURRENT)
#TestTemplate
#ExtendWith(ScTestExtension::class)
annotation class ScTest(
// ... some properties
)
class ScTestExtension : TestTemplateInvocationContextProvider {
override fun supportsTestTemplate(context: ExtensionContext): Boolean {
return true
}
override fun provideTestTemplateInvocationContexts(extensionContext: ExtensionContext): Stream<TestTemplateInvocationContext> {
// ... return invocation context
}
}
Your ScTest.java should look similar to this one:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Test;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Test
public #interface ScTest {}
Derived from https://junit.org/junit5/docs/current/user-guide/#writing-tests-meta-annotations
IIRC, IDEA and other IDEs look for an instance of Testable to display the "Run..." icon. But normally, you shouldn't use that annotation directly. It should suffice to have it meta-annotated with #Test, #TestTemplate, or friends.
Here's another example using #TestTemplate that worked (at least, two years ago): CartesianProductTest.java
#TestTemplate
#ExtendWith(CartesianProductProvider.class)
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CartesianProductTest {...}
Here is how "IntelliJ IDEA 2019.3 EAP (Community Edition) Build #IC-193.4386.10, built on October 9, 2019" shows the usage of the #CartesianProductTest annotation today:
Perhaps you need to switch to dark mode...
Because of problems with Powermock and Java 11, we have had to use the #PowerMockIgnore on all our test classes.
#PowerMockIgnore({ "javax.management.*", "com.sun.org.apache.xerces.*", "javax.xml.*",
"org.xml.*", "org.w3c.dom.*", "com.sun.org.apache.xalan.*", "javax.activation.*" })
In order to avoid duplicating this in all test classes, we created a custom annotation to be used instead that had this PowerMockIgnore defined on one place.
So that all we would need in all our test classes would be
#SuppressPowerMockInitError
However: This custom annotation does not seem to work.
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
#Retention(RUNTIME)
#Target({ TYPE, METHOD })
#Inherited
#Documented
#PowerMockIgnore({ "javax.management.*", "com.sun.org.apache.xerces.*", "javax.xml.*",
"org.xml.*", "org.w3c.dom.*", "com.sun.org.apache.xalan.*", "javax.activation.*" })
public #interface SuppressPowerMockInitError {
}
It does not look like it is possible to achieve this with annotations.
However creating an abstract class that has this common annotation seems like the best way to achieve this.
import org.powermock.core.classloader.annotations.PowerMockIgnore;
#PowerMockIgnore({ "javax.management.*", "com.sun.org.apache.xerces.*", "javax.xml.*",
"org.xml.*", "org.w3c.dom.*", "com.sun.org.apache.xalan.*", "javax.activation.*" })
public abstract class AbstractPowerMockTest {
}
There are some modules that contain methods which are marked with the following annotation:
#SuppressWarnings({"WeakerAccess"})
When this annotation is added,
the compiler does not give warning about the access modifier can be more private.
Some of the time this is done because some child class (outside the module) will override the method.
When this is the case,
we are required to add a comment stating that the method be overridden.
Now,
for every method and variable,
we have two things,
the annotation and the comment.
Does Java let us create an annotation which can indicate both cases.
This is what I have:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
#Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
#Retention(RetentionPolicy.SOURCE)
#SuppressWarnings({"WeakerAccess"})
public #interface WillBeOverriden { } // Ignore the ugly name
The goal of this new annotation is that it will behave exactly the same as the SuppressWarnings annotation and removes the need to add a java comment; since the annotation will act as the comment.
This stackoverflow question seems to have the answer,
but my compiler still complains that the field can be more private.
the standard use of these works great, #TmsLink("foo") #Issue("bar) However when using a dataProvider scenario, how can I programmatically set these values on each iteration of the #Test method?
Each csv entry for my test data will contain both an Id for TmsLink and Issue, how can I make each test set its own for good reporting purposes? We can do it with SeverityLevel which looks like a simple enum, however Issue/TmsLink are interfaces with #Target at Method/Type only
SeverityLevel severity = severityLevel.CRITICAL;
package io.qameta.allure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Used to link tests with issues.
*/
#Documented
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
#Repeatable(Issues.class)
public #interface Issue {
String value();
}
Thank you.
to anyone coming along to this at a later stage, I figured it out by overriding the annotation values at runtime, its not amazing by any stretch but it works, I wrote up a blog post on the subject:
https://symonk.github.io/2017-10-26-overriding-allure-reporting-annotational-values-at-runtime-for-issue-tmslink-displayname/
In this code we have one user defined Annotation but without body any method what could be use of this
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.SOURCE)
#Target( { ElementType.TYPE })
public #interface InputBean {
}
The #Retention(RetentionPolicy.SOURCE) tells us that whatever use it has, it's only at the source code level -- the annotation is forgotten by the time the compiler is done, and isn't in the generated class files.
That leaves two options:
as a visual clue (ie, documentation) to human coders
to be used by an annotation processor
Without knowing more, we can't tell which of those is the case here.