Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(#1016): Enhance bean definition parser configuration capabilities #1017

Merged
merged 1 commit into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}
}
}
Loading