From a57e811e234d9505cafbfa65b16841891894bccf Mon Sep 17 00:00:00 2001 From: Thorsten Schlathoelter Date: Fri, 13 Oct 2023 06:33:52 +0200 Subject: [PATCH] fix(#1016): Enhance bean definition parser configuration capabilities --- .../common/SpringXmlTestLoader.java | 69 ++++++++++++++++--- .../common/SpringXmlTestLoaderConfigurer.java | 18 +++++ .../spring/SpringXmlTestLoader_IT.java | 26 ++++++- 3 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoaderConfigurer.java diff --git a/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoader.java b/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoader.java index b1ddb971d0..a8ee395b75 100644 --- a/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoader.java +++ b/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoader.java @@ -19,6 +19,8 @@ import java.io.File; import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; import org.citrusframework.CitrusSpringContext; import org.citrusframework.DefaultTestCaseRunner; import org.citrusframework.TestCase; @@ -49,8 +51,8 @@ protected void doLoad() { try { testCase = ctx.getBean(testName, TestCase.class); - if (runner instanceof DefaultTestCaseRunner) { - ((DefaultTestCaseRunner) runner).setTestCase(testCase); + if (runner instanceof DefaultTestCaseRunner defaultTestCaseRunner) { + defaultTestCaseRunner.setTestCase(testCase); } configurer.forEach(handler -> handler.accept(testCase)); @@ -68,13 +70,14 @@ protected void doLoad() { */ private ApplicationContext loadApplicationContext() { try { - configureCustomParsers(); + ApplicationContext parentApplicationContext = getParentApplicationContext(); + configureCustomParsers(parentApplicationContext); return new ClassPathXmlApplicationContext( new String[]{ getSource(), "org/citrusframework/spring/annotation-config-ctx.xml"}, - true, getParentApplicationContext()); + true, parentApplicationContext); } catch (Exception e) { throw citrusContext.getTestContextFactory().getObject() .handleError(testName, packageName, "Failed to load test case", e); @@ -84,9 +87,11 @@ private ApplicationContext loadApplicationContext() { /** * Configures the CitrusNamespaceParserRegistry with custom parsers */ - private void configureCustomParsers() { - SpringXmlTestLoaderConfiguration loaderConfiguration = testClass.getAnnotation(SpringXmlTestLoaderConfiguration.class); - if (loaderConfiguration != null) { + private void configureCustomParsers(ApplicationContext parentApplicationContext) { + List beanDefinitionParserConfigurationList = retrieveSpringXmlTestLoaderConfigurations( + parentApplicationContext); + + for (SpringXmlTestLoaderConfiguration loaderConfiguration : beanDefinitionParserConfigurationList) { for (BeanDefinitionParserConfiguration beanDefinitionParserConfiguration : loaderConfiguration.parserConfigurations()) { Class parserClass = beanDefinitionParserConfiguration.parser(); try { @@ -99,11 +104,57 @@ private void configureCustomParsers() { } } } + + } + + /** + * Retrieves a collection of optional SpringXmlTestLoaderConfigurations. This collection is composed of: + * + * + * @param applicationContext The parent application context. + * @return A collection of SpringXmlTestLoaderConfigurations. + * + * @see SpringXmlTestLoaderConfigurer + */ + private List retrieveSpringXmlTestLoaderConfigurations( + ApplicationContext applicationContext) { + List beanDefinitionParserConfigurationList = new ArrayList<>(); + + addOptionalTestClassLevelAnnotation( + testClass, + beanDefinitionParserConfigurationList); + addOptionalConfigurerClassLevelAnnotations(applicationContext, + beanDefinitionParserConfigurationList); + + return beanDefinitionParserConfigurationList; + } + + private void addOptionalConfigurerClassLevelAnnotations(ApplicationContext applicationContext, + List beanDefinitionParserConfigurationList) { + if (applicationContext != null) { + applicationContext.getBeansOfType( + SpringXmlTestLoaderConfigurer.class).values().forEach(configurer -> + addOptionalTestClassLevelAnnotation(configurer.getClass(), + beanDefinitionParserConfigurationList) + ); + } + } + + private void addOptionalTestClassLevelAnnotation(Class testClass, + List beanDefinitionParserConfigurationList) { + + SpringXmlTestLoaderConfiguration loaderConfiguration = testClass.getAnnotation(SpringXmlTestLoaderConfiguration.class); + if (loaderConfiguration != null) { + beanDefinitionParserConfigurationList.add(loaderConfiguration); + } } private ApplicationContext getParentApplicationContext() { - if (citrusContext instanceof CitrusSpringContext) { - return ((CitrusSpringContext) citrusContext).getApplicationContext(); + if (citrusContext instanceof CitrusSpringContext citrusSpringContext) { + return citrusSpringContext.getApplicationContext(); } return null; diff --git a/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoaderConfigurer.java b/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoaderConfigurer.java new file mode 100644 index 0000000000..10cc5346af --- /dev/null +++ b/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoaderConfigurer.java @@ -0,0 +1,18 @@ +package org.citrusframework.common; + +/** + * A marker interface for identifying beans that supply a {@link SpringXmlTestLoaderConfiguration} + * annotation to configure the bean definition parser for SpringXmlTest parsing. + *

+ * To define specific bean definition parser configurations, you can implement this interface + * along with the corresponding {@link SpringXmlTestLoaderConfiguration} annotation and provide it + * as a bean through the parent {@link org.springframework.context.ApplicationContext} used for + * loading SpringXmlTest. + * + * + * @author Thorsten Schlathoelter + * @since 4.0 + * @see SpringXmlTestLoader + */ +public interface SpringXmlTestLoaderConfigurer { +} diff --git a/runtime/citrus-junit5/src/test/java/org/citrusframework/junit/jupiter/integration/spring/SpringXmlTestLoader_IT.java b/runtime/citrus-junit5/src/test/java/org/citrusframework/junit/jupiter/integration/spring/SpringXmlTestLoader_IT.java index 9cba0f81a2..1570d1456a 100644 --- a/runtime/citrus-junit5/src/test/java/org/citrusframework/junit/jupiter/integration/spring/SpringXmlTestLoader_IT.java +++ b/runtime/citrus-junit5/src/test/java/org/citrusframework/junit/jupiter/integration/spring/SpringXmlTestLoader_IT.java @@ -7,13 +7,17 @@ import org.citrusframework.annotations.CitrusXmlTest; import org.citrusframework.common.BeanDefinitionParserConfiguration; import org.citrusframework.common.SpringXmlTestLoaderConfiguration; +import org.citrusframework.common.SpringXmlTestLoaderConfigurer; import org.citrusframework.config.CitrusSpringConfig; import org.citrusframework.config.xml.BaseTestCaseMetaInfoParser; import org.citrusframework.config.xml.BaseTestCaseParser; +import org.citrusframework.junit.jupiter.integration.spring.SpringXmlTestLoader_IT.CustomConfiguration; import org.citrusframework.junit.jupiter.spring.CitrusSpringSupport; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; @@ -22,11 +26,10 @@ * @author Thorsten Schlathoelter */ @CitrusSpringSupport -@ContextConfiguration(classes = {CitrusSpringConfig.class}) +@ContextConfiguration(classes = {CitrusSpringConfig.class, CustomConfiguration.class}) @SpringXmlTestLoaderConfiguration( parserConfigurations = { @BeanDefinitionParserConfiguration(name = "testcase", parser = SpringXmlTestLoader_IT.CustomTestCaseParser.class), - @BeanDefinitionParserConfiguration(name = "meta-info", parser = SpringXmlTestLoader_IT.CustomTestCaseMetaInfoParser.class) }) public class SpringXmlTestLoader_IT { @@ -37,7 +40,7 @@ public void SpringXmlTestLoaderIT_0_IT(@CitrusResource TestCaseRunner runner) { TestCaseMetaInfo metaInfo = runner.getTestCase().getMetaInfo(); Assertions.assertTrue(metaInfo instanceof CustomTestCaseMetaInfo); - Assertions.assertEquals(((CustomTestCaseMetaInfo)metaInfo).getDescription(), "Foo bar: F#!$§ed up beyond all repair"); + Assertions.assertEquals( "Foo bar: F#!$§ed up beyond all repair", ((CustomTestCaseMetaInfo)metaInfo).getDescription()); } /** @@ -91,8 +94,25 @@ protected void parseAdditionalProperties(Element metaInfoElement, BeanDefinition metaInfoBuilder.addPropertyValue("description", description); } } + } + /** + * A loader configuration that can be added as a bean and that will be picked up for configuration + * of any XML test case. + */ + @SpringXmlTestLoaderConfiguration( + parserConfigurations = { + @BeanDefinitionParserConfiguration(name = "meta-info", parser = SpringXmlTestLoader_IT.CustomTestCaseMetaInfoParser.class) + }) + public static class CustomTestLoaderConfigurer implements SpringXmlTestLoaderConfigurer { } + @Configuration + public static class CustomConfiguration { + @Bean + public CustomTestLoaderConfigurer customTestLoaderConfigurer() { + return new CustomTestLoaderConfigurer(); + } + } }