Merge multiple #TestExecutionListeners from different declarations? - java

For the integration tests of my Spring Boot app, I have declared custom meta-annotations (a bit like Spring Boot's test slice annotations). How can I declare different TestExecutionListeners in each meta-annotation, and have all of them merged when running a test class?
I can only find mergeMode = MERGE_WITH_DEFAULTS which merges the declared TestExecutionListener with the default ones, but not different custom Listeners declared in different places.
A minimal example:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#TestExecutionListeners(
listeners = DbTestListener.class,
mergeMode = MERGE_WITH_DEFAULTS)
public #interface DbIntegrationTest {
}
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#TestExecutionListeners(
listeners = MessagingTestListener.class,
mergeMode = MERGE_WITH_DEFAULTS)
public #interface MessagingIntegrationTest {
}
#RunWith(SpringRunner.class)
#DbIntegrationTest
#MessagingIntegrationTest
public class ExampleTest {
// test cases here
}
As a result, I'd like to have both custom TestListeners and the default ones executed for my ExampleTest.
Clarification: the above example is minimal as to show what I want. Of course like that it doesn't make much sense. My own composed annotations have much more setup in them which I haven't shown, and I have multiple layers of meta-annotations.

There is no such features in the current version of spring test.
If you think more deeply about your ideas, it will introduce ambiguous in some cases. For example, if #DbIntegrationTest and #MessagingIntegrationTest are configured with different values of mergeMode or inheritListeners, which value should the framework use?
The current behaviour is that if there are multiple #TestExecutionListeners or its meta-annotation are marked , only the topmost declared one will take effect while the other will be ignored.
Some ideas which may help you to achieve the similar result :
Use a meta-annotation to predefine some kind of profile which groups several TestExecutionListeners that will be used together in different test scenario. Just annotate this single meta-annotation to the test class. For example :
#MessagingTest contains all the required listeners for messaging integration test :
#TestExecutionListeners(
listeners = {SetupMessageBrokerListener.class, FooBarListener.class } ,
mergeMode = MERGE_WITH_DEFAULTS)
public #interface MessagingTest {
}
#DatabaseTest contains all the required listeners for DB integration test :
#TestExecutionListeners(
listeners = {SetupDatabaseListener.class, CreateTestingDataListener.class , FooBarListener.class } ,
mergeMode = MERGE_WITH_DEFAULTS)
public #interface DatabaseTest{
}
Implement your own version of TestContextBootstrapper. What TestExecutionListener to be created is defined in its getTestExecutionListeners(). Use #BootstrapWith to activate this TestContextBootstrapper. AbstractTestContextBootstrapper is the good starting point . Basically , you need to add your customised logic at the end of getTestExecutionListeners() . Sadly, this method is finalised which does not allow to override in the sub-classes...

A possible solution is to register all TestExecutionListeners in META-INF/spring.factories, and write each Listener so that it only does something if some annotation (or other marker) is present on the test class or in the test context.
For usage of spring.factories see: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-tel-config-automatic-discovery
The disadvantage of this solution is that the setup of TestExecutionListeners (which are defined and which will be active) is a bit "hidden" or spread out in the code base.

What do these listeners do? If provide execution environment then you misuse listener API.
To configure (and cleanup) environment you should use
#Rule \ #ClassRule if you use Junit4
#ExtendWith if you use Junit5
These are easily combined within single test.

Related

Is there any way to find #Disabled Tests in a class of Java test class in Junit5?

Is there any way to find #Disabled Tests in a class of Java test class in Junit5 ? I am using junit.platform.launcher to discover test cases in my project. I am creating a TestPlan and trying to find the tests which are disabled but cant see anything there to help me.
Disabling tests is an execution time feature since disabling/enabling may depend on stuff that’s only available during execution. That’s why the test identifiers in the test plan don’t show it.
All you can do is check for the annotation being present on a method or a container class. Mind that the annotation can be „meta present“ ie the annotation of another annotation. Some methods in https://junit.org/junit5/docs/current/api/org.junit.platform.commons/org/junit/platform/commons/support/AnnotationSupport.html are probably useful to reduce the amount of boilerplate code to detect all cases.
I found the solution by using Reflection and annotations class to get the annotations. Once I received the list of annotations, I used a if condition to check if there is any Disabled tag present in the test case or not.
`Class clazz = Class.forName (className);
Method[] m = clazz.getDeclaredMethods ();
for(Method method : m){
if(method.getName ().equalsIgnoreCase (methodName)) {
Annotation[] annotations = method.getAnnotations ();
for(Annotation anno : annotations) {
if(anno.annotationType ().getName ().equalsIgnore("org.junit.jupiter.api.Disabled")){
// Do Something
}
}
}
}

Spring DeferredImportSelector run before any Configurations

I am trying to create something like #Enable... autoconfiguration. I wanted to create such an annotation for a custom library that has an extensive spring config, yet needs like 2 beans supplied, and based on those initializes various contexts. It indeed initializes beans in all #configuration classes that are returned in an array, but I also want to do some custom configauration logic based on already registered beans. Now the javadoc for that
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ImportSelector.html
States that
ImportSelectors are usually processed in the same way as regular #Import annotations, however, it is also possible to defer selection of imports until all #Configuration classes have been processed (see DeferredImportSelector for details).
So I turned to DeferredImportSelector cause that Selector is said to be run after all #Configuration beans so I can do conditional Beans. Now javadoc is quite clear here (https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/DeferredImportSelector.html)
A variation of ImportSelector that runs after all #Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are #Conditional
So this is perfect for me. Well until it turns out that no matter what I do, the import selector selectImports method is run always as first before all #Configuration beans.
I figured out that maybe the main #Configuration bean is always last, and the javadoc actually mentions all imported #Configuration beans. It appears that is also not the case. I have been checking precedence using the debugger, but here is the test code I did, and it is pretty simple:
The import selector that does nothing except system out:
public class TestImportSelector implements DeferredImportSelector{
#Override
public String[] selectImports(AnnotationMetadata arg0) {
System.out.println("ImportSelector");
return new String[0];
}
}
The configuration class (the imported to check if any of mentioned ideas work)
#Configuration
public class ImportedTestContext {
#Bean
public String testBeanString2(){
System.out.println("bean2");
return "string2";
}
}
The main context class (also tried swapping the imported classes)
#Configuration
#Import({TestImportSelector.class, ImportedTestContext.class})
public class TestMainContext {
#Bean
public String testBeanString(){
System.out.println("bean1");
return "string";
}
}
And finally my main class
public class Test {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(TestMainContext.class);
}
}
Sooo, as You run the main. You always get the same output
ImportSelector
bean2
bean1
The import selector is always first no matter what. Additionally I tried to mess around with Ordered interface as the javadoc says
Implementations can also extend the Ordered interface or use the Order annotation to indicate a precedence against other DeferredImportSelectors
But it appears that getOrder method is not even called. Well that might be because it says that it only checks it agains other DeferredImportSelectors (and there were none but was worth trying)
I have done this with spring-context 4.3.2.RELEASE as this is what is used in my project, but just to make sure also tested with 5.0.5.RELEASE.
Exactly the same result.
So I believe I do not understand something with regards to ImportSelector, DeferredImportSelector, spring or there is a little chance that the javadoc is not saying the truth or I misunderstood it....
I would appreciate any help or advice....
Just to make it clear: Based on that DeferredImportSelector, I want it to implement BeanFactoryAware (this part works, Spring indeed injects the BeanFactory) that would check what beans were already defined (like those funny test string beans) and based on that will tell spring what additional configurations should be loaded. Based on javadoc this is what it was made for.....
It looks like DeferredImportSelector has a little bit unclear documentation. After running a few tests and checking the code, it turns out that what is deferred is Import, not ImportSelector.
So, if you make use of DeferredImportSelector, you can select a configuration class(es) which import will be deferred.
A selectImports method will get executed normally - during configuration files resolving/parsing, so - using BeanFactory to check if other beans' definitions are already loaded would be certainly a bad idea (as some might not yet be).
The best approach would be to put this logic into #Conditional annotations family (within a target configuration class) and make sure it will be processed after all user-defined configurations.

How to get Spring profile name from annotation?

With Spring 3.1 and profiles, creating a custom interface to define specific profiles becomes interesting. Part of the beauty is the ability to completely forgot the String name of the profile and just use the annotation.
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Profile("Dev")
public #interface Dev {
}
And then simply annotate beans with #Dev. This works great.
However, how can I check if the #Dev profile is active? Environment.acceptsProfiles() requires a String argument. Is there a "neat" way of doing this, or is my only option to do something like:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Profile(Dev.NAME)
public #interface Dev {
public static String NAME = "Dev";
}
public class MyClass{
#Autowired
private Environment env;
private void myMethod(){
if( env.acceptsProfiles( Dev.NAME ) )
// do something here
;
}
Although functional, I'm not particularly fond of this concept. Is there another way I can do this neater?
I wanted to do something similar (in my case, represent a list of synonyms under one profile annotation) but I ran into the problem your having, as well as another limitation: You won't be able to apply more than one of the annotations to a single bean and have them both get picked up by spring (at least in spring 3).
Unfortunately, as you cannot pass the enum in, the solution I settled on was to just use plain-old string constants without the enum. Then I could do something like #Profile(CONSTANT_ONE, CONSTANT_TWO). I still benefited from not being able to make typos, but also gained the ability to still apply multiple profiles to the same bean.
Not perfect, but not too bad.

How to get custom annotation from source code in java?

I'm trying to use a custom annotation to get some statistics for unit test. Another guy has defined a custom annotation as follows:
#Retention(RetentionPolicy.SOURCE)
#Target( { ElementType.METHOD, ElementType.TYPE})
public #interface TimsID {
String id();
String description() default "";
}
What I need to do is extracting this annotation from all unit tests in our project.
Here comes the problem:
the RetentionPolicy is defined as SOURCE, I don't know how to get it in the unit test.
I know that if it's a RUNTIME, it may be read reflectively like this:
Class<TestExample> obj = TestExample.class;
// Process #TimsID
if (obj.isAnnotationPresent(TimsID.class)) {
Annotation annotation = obj.getAnnotation(TimsID.class);
TimsID TimsID = (TimsID) annotation;
}
But now it's 'SOURCE', annotations will not be recorded in the class file by the compiler or retained by the VM at run time, so they can't be read reflectively.
The guy who defined the custom annotation said the reason he chooses "SOURCE" is that we just need to statistic this annotation in source code, we don't need to write these custom annotations in class file or even runtime, so we need annotation analysis only in source code.
I've accomplished this work, and here is the step and code.
SOURCE retention is aimed to be used only during compilation process. You may look into APT (Annotation Processing Tool) for more information on how to perform such kind of compile-time annotation processing logic. (However I wonder if it can do what you want)
You'll have to change the RetentionPolicy in the source code, unfortunately. There's no other way to make the annotation available for reflection at runtime, even in tests.

Java: Annotated Annotations (and passing values)

I have a annotation that does include several other annotations, pretty much like this one here:
#Component // Spring Component
#Interface OsgiService { boolean isFactory() }
meaning that all classes annotated with #OsgiService shall automatically also be annotated as #Component. Which works fine.
Now however, I'd like to add another annotation, that has a parameter which is dependent of the isFactory parameter of #OsgiService.
#Component // Spring Component
#Scope(isFactory() ? "prototype" : "singleton")
#Interface OsgiService { boolean isFactory() }
Which does not work. However, as isFactory property of an annotation requires to be a static value, shouldn't it be possible to have something like this?
I don't think this is possible.
You can create two annotations: #OsgiService and #OsgiServiceFactory

Categories

Resources