Skip to content

Commit

Permalink
fix(#1016): Enhance bean definition parser configuration capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
Thorsten Schlathoelter committed Oct 13, 2023
1 parent 12aa556 commit 24f0ccb
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand All @@ -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);
Expand All @@ -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<SpringXmlTestLoaderConfiguration> beanDefinitionParserConfigurationList = retrieveSpringXmlTestLoaderConfigurations(
parentApplicationContext);

for (SpringXmlTestLoaderConfiguration loaderConfiguration : beanDefinitionParserConfigurationList) {
for (BeanDefinitionParserConfiguration beanDefinitionParserConfiguration : loaderConfiguration.parserConfigurations()) {
Class<? extends BeanDefinitionParser> parserClass = beanDefinitionParserConfiguration.parser();
try {
Expand All @@ -99,11 +104,57 @@ private void configureCustomParsers() {
}
}
}

}

/**
* Retrieves a collection of optional SpringXmlTestLoaderConfigurations. This collection is composed of:
* <ul>
* <li>Annotations at the class level of the current test being executed.</li>
* <li>Annotations provided by all {@link SpringXmlTestLoaderConfigurer} beans found in the application context.</li>
* </ul>
*
* @param applicationContext the application context that optionally provides {@link SpringXmlTestLoaderConfigurer} beans
* @return a collection of SpringXmlTestLoaderConfigurations
*
* @see SpringXmlTestLoaderConfigurer
*/
private List<SpringXmlTestLoaderConfiguration> retrieveSpringXmlTestLoaderConfigurations(
ApplicationContext applicationContext) {
List<SpringXmlTestLoaderConfiguration> beanDefinitionParserConfigurationList = new ArrayList<>();

addOptionalTestClassLevelAnnotation(
testClass,
beanDefinitionParserConfigurationList);
addOptionalConfigurerClassLevelAnnotations(applicationContext,
beanDefinitionParserConfigurationList);

return beanDefinitionParserConfigurationList;
}

private void addOptionalConfigurerClassLevelAnnotations(ApplicationContext applicationContext,
List<SpringXmlTestLoaderConfiguration> beanDefinitionParserConfigurationList) {
if (applicationContext != null) {
applicationContext.getBeansOfType(
SpringXmlTestLoaderConfigurer.class).values().forEach(configurer ->
addOptionalTestClassLevelAnnotation(configurer.getClass(),
beanDefinitionParserConfigurationList)
);
}
}

private void addOptionalTestClassLevelAnnotation(Class<?> testClass,
List<SpringXmlTestLoaderConfiguration> 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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* 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 {
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,26 @@
*/
@CitrusSpringSupport
@ContextConfiguration(classes = {CitrusSpringConfig.class})
public class SpringBeanXml_IT {
class SpringBeanXml_IT {

@Test
@DisplayName("SpringBeanXml_IT")
@CitrusXmlTest(name = "SpringBeanXml_IT")
public void SpringBeanXml_0_IT() {
void SpringBeanXml_0_IT() {
}

@Test
@CitrusTestSource(type = TestLoader.GROOVY, name = "echo.test", packageName = "org.citrusframework.junit.jupiter.simple")
public void SpringGroovy_IT() {
void SpringGroovy_IT() {
}

@Test
@CitrusXmlTest(name = "SampleIT")
public void SpringBeanXml_1_IT() {
void SpringBeanXml_1_IT() {
}

@CitrusSpringXmlTestFactory
public Stream<DynamicTest> SpringBeanXml_2_IT() {
Stream<DynamicTest> SpringBeanXml_2_IT() {
return Stream.of(
CitrusTestFactorySupport.springXml().dynamicTest("org.citrusframework.junit.jupiter.integration.actions", "EchoActionIT"),
CitrusTestFactorySupport.springXml().dynamicTest("org.citrusframework.junit.jupiter.integration.actions", "FailActionIT"),
Expand All @@ -66,12 +66,12 @@ public Stream<DynamicTest> SpringBeanXml_2_IT() {
}

@CitrusSpringXmlTestFactory
public Stream<DynamicTest> SpringBeanXml_3_IT() {
Stream<DynamicTest> SpringBeanXml_3_IT() {
return CitrusTestFactorySupport.springXml().packageScan("org.citrusframework.junit.jupiter.simple");
}

@Test
@CitrusXmlTest(sources = "classpath:org/citrusframework/junit/jupiter/integration/spring/SampleIT.xml")
public void SpringBeanXml_4_IT() {
void SpringBeanXml_4_IT() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
*/
@CitrusSpringSupport
@ContextConfiguration(classes = {CitrusSpringConfig.class, SpringBean_IT.EndpointConfig.class})
public class SpringBean_IT {
class SpringBean_IT {

@Autowired
private DirectEndpoint direct;
Expand All @@ -65,7 +65,7 @@ void springBeanTest(@CitrusResource TestActionRunner actions) {
}

@Configuration
public static class EndpointConfig {
static class EndpointConfig {

@Bean
public BeforeTest beforeTest() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -22,34 +26,33 @@
* @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 {
class SpringXmlTestLoader_IT {

@Test
@CitrusXmlTest(name="SpringXmlTestLoader_IT")
public void SpringXmlTestLoaderIT_0_IT(@CitrusResource TestCaseRunner runner) {
void SpringXmlTestLoaderIT_0_IT(@CitrusResource TestCaseRunner runner) {
Assertions.assertNotNull(runner.getTestCase());

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());
}

/**
* A custom test case implementation that should be created by the loader
*/
public static class CustomTestCase extends DefaultTestCase {
static class CustomTestCase extends DefaultTestCase {
}

/**
* A custom test case meta info that should be created by the loader
*/
public static class CustomTestCaseMetaInfo extends TestCaseMetaInfo {
static class CustomTestCaseMetaInfo extends TestCaseMetaInfo {

private String description;

Expand Down Expand Up @@ -91,8 +94,29 @@ 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)
})
static class CustomTestLoaderConfigurer implements SpringXmlTestLoaderConfigurer {
}

/**
* A configuration that provides the CustomTestLoaderConfigurer bean for configuration of
* all SpringXmlTest cases.
*/
@Configuration
static class CustomConfiguration {

@Bean
public CustomTestLoaderConfigurer customTestLoaderConfigurer() {
return new CustomTestLoaderConfigurer();
}
}
}

0 comments on commit 24f0ccb

Please sign in to comment.