diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a213a2d7a7..72451af6647 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# v0.14.0 + +## Spring Integration: Backwards Incompatible Changes + +In order to fix issue #259 the Spring integration was largely rewritten. If you only had used the `@EnableJGiven` and `@JGivenStage` annotations, nothing should change. +If you had a custom Spring configuration for JGiven you have to change the following: + +* The classes `SpringStepMethodInterceptor` and `JGivenStageAutoProxyCreator` do not exist anymore, you have to remove all references +* As a replacement the new class `JGivenBeanFactoryPostProcessor` exists now. You have to register this bean in your Spring configuration + +## Fixed Issues + +* Spring Integration: Nested Steps are now supported when using Spring [#259](https://github.com/TNG/JGiven/issues/259) + # v0.13.0 ## Backwards Incompatible Changes diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ByteBuddyStageCreator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ByteBuddyStageCreator.java index 82161036263..05a654357b4 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ByteBuddyStageCreator.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ByteBuddyStageCreator.java @@ -1,6 +1,9 @@ package com.tngtech.jgiven.impl; +import static net.bytebuddy.matcher.ElementMatchers.any; + import com.tngtech.jgiven.impl.intercept.ByteBuddyMethodInterceptor; +import com.tngtech.jgiven.impl.intercept.StageInterceptorInternal; import com.tngtech.jgiven.impl.intercept.StepInterceptor; import net.bytebuddy.ByteBuddy; @@ -8,40 +11,42 @@ import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; import net.bytebuddy.implementation.MethodDelegation; -import static net.bytebuddy.matcher.ElementMatchers.any; - public class ByteBuddyStageCreator implements StageCreator { @SuppressWarnings( "unchecked" ) @Override - public T createStage(Class stageClass, StepInterceptor stepInterceptor) { + public T createStage( Class stageClass, StepInterceptor stepInterceptor ) { try { - T result = new ByteBuddy() - .subclass(stageClass, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) - .method(any()) - .intercept(MethodDelegation.to(new ByteBuddyMethodInterceptor(stepInterceptor))) - .make() - .load(getClassLoader(stageClass), - getClassLoadingStrategy(stageClass)) - .getLoaded() - .newInstance(); + T result = createStageClass( stageClass, stepInterceptor ) + .newInstance(); return result; - } catch (Error e) { + } catch( Error e ) { throw e; - } catch (Exception e) { - throw new RuntimeException("Error while trying to create an instance of class "+stageClass, e); + } catch( Exception e ) { + throw new RuntimeException( "Error while trying to create an instance of class " + stageClass, e ); } } - protected ClassLoadingStrategy getClassLoadingStrategy(Class stageClass) { - return getClassLoader(stageClass) == null + public Class createStageClass( Class stageClass, StepInterceptor stepInterceptor ) { + return new ByteBuddy() + .subclass( stageClass, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING ) + .implement( StageInterceptorInternal.class ) + .method( any() ) + .intercept( MethodDelegation.to( new ByteBuddyMethodInterceptor( stepInterceptor ) ) ) + .make() + .load( getClassLoader( stageClass ), + getClassLoadingStrategy( stageClass ) ) + .getLoaded(); + } + + protected ClassLoadingStrategy getClassLoadingStrategy( Class stageClass ) { + return getClassLoader( stageClass ) == null ? ClassLoadingStrategy.Default.WRAPPER : ClassLoadingStrategy.Default.INJECTION; } - protected ClassLoader getClassLoader(Class stageClass) { + protected ClassLoader getClassLoader( Class stageClass ) { return Thread.currentThread().getContextClassLoader(); } - } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/ByteBuddyMethodInterceptor.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/ByteBuddyMethodInterceptor.java index 369a8781609..dda8c19bdb4 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/ByteBuddyMethodInterceptor.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/ByteBuddyMethodInterceptor.java @@ -1,10 +1,10 @@ package com.tngtech.jgiven.impl.intercept; -import com.tngtech.jgiven.impl.intercept.StepInterceptor.Invoker; - import java.lang.reflect.Method; - import java.util.concurrent.Callable; + +import com.tngtech.jgiven.impl.intercept.StepInterceptor.Invoker; + import net.bytebuddy.implementation.bind.annotation.AllArguments; import net.bytebuddy.implementation.bind.annotation.BindingPriority; import net.bytebuddy.implementation.bind.annotation.DefaultCall; @@ -19,17 +19,28 @@ */ public class ByteBuddyMethodInterceptor { - private final StepInterceptor interceptor; + private StepInterceptor interceptor; - public ByteBuddyMethodInterceptor(StepInterceptor interceptor ) { + public ByteBuddyMethodInterceptor() {} + + public ByteBuddyMethodInterceptor( StepInterceptor interceptor ) { + this.interceptor = interceptor; + } + + public void setInterceptor( StepInterceptor interceptor ) { this.interceptor = interceptor; } @RuntimeType - @BindingPriority(BindingPriority.DEFAULT * 3) - public Object interceptSuper( @SuperCall final Callable zuper,@This final Object receiver,@Origin - Method method, @AllArguments final Object[] parameters) - throws Throwable { + @BindingPriority( BindingPriority.DEFAULT * 3 ) + public Object interceptSuper( @SuperCall final Callable zuper, @This final Object receiver, @Origin Method method, + @AllArguments final Object[] parameters ) + throws Throwable { + + if( handleSetStepInterceptor( method, parameters ) ) { + return null; + } + Invoker invoker = new Invoker() { @Override public Object proceed() throws Throwable { @@ -37,27 +48,46 @@ public Object proceed() throws Throwable { } }; - return interceptor.intercept( receiver , method, parameters, invoker ); + return interceptor.intercept( receiver, method, parameters, invoker ); } @RuntimeType - @BindingPriority(BindingPriority.DEFAULT * 2) - public Object interceptDefault(@DefaultCall final Callable zuper,@This final Object receiver,@Origin - Method method, @AllArguments final Object[] parameters) - throws Throwable { - Invoker invoker = new Invoker() { + @BindingPriority( BindingPriority.DEFAULT * 2 ) + public Object interceptDefault( @DefaultCall final Callable zuper, @This final Object receiver, @Origin Method method, + @AllArguments final Object[] parameters ) + throws Throwable { + + if( handleSetStepInterceptor( method, parameters ) ) { + return null; + } + Invoker invoker = new Invoker() { @Override public Object proceed() throws Throwable { return zuper.call(); } }; - return interceptor.intercept( receiver , method, parameters, invoker ); + return interceptor.intercept( receiver, method, parameters, invoker ); + } + + private boolean handleSetStepInterceptor( Method method, final Object[] parameters ) { + if( method.getName().equals( "setStepInterceptor" ) && method.getDeclaringClass().equals( StageInterceptorInternal.class ) ) { + setInterceptor( (StepInterceptor) parameters[0] ); + return true; + } + return false; } + @RuntimeType - public Object intercept( @This final Object receiver,@Origin final Method method, @AllArguments final Object[] parameters) throws Throwable { + public Object intercept( @This final Object receiver, @Origin final Method method, @AllArguments final Object[] parameters ) + throws Throwable { // this intercepted method does not have a non-abstract super method + + if( handleSetStepInterceptor( method, parameters ) ) { + return null; + } + Invoker invoker = new Invoker() { @Override @@ -66,8 +96,7 @@ public Object proceed() throws Throwable { } }; - return interceptor.intercept( receiver , method, parameters, invoker ); + return interceptor.intercept( receiver, method, parameters, invoker ); } - } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/StageInterceptorInternal.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/StageInterceptorInternal.java new file mode 100644 index 00000000000..ae89d73e78e --- /dev/null +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/StageInterceptorInternal.java @@ -0,0 +1,5 @@ +package com.tngtech.jgiven.impl.intercept; + +public interface StageInterceptorInternal { + void setStepInterceptor( StepInterceptor stepInterceptor ); +} diff --git a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/AnnotationDrivenBeanDefinitionParser.java b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/AnnotationDrivenBeanDefinitionParser.java index 5869a1cf18b..eb21209774c 100644 --- a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/AnnotationDrivenBeanDefinitionParser.java +++ b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/AnnotationDrivenBeanDefinitionParser.java @@ -6,7 +6,6 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.parsing.CompositeComponentDefinition; -import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; @@ -24,31 +23,19 @@ public BeanDefinition parse( Element element, ParserContext parserContext ) { if( !parserContext.getRegistry().containsBeanDefinition( BEAN_NAME ) ) { Object eleSource = parserContext.extractSource( element ); - // create Interceptor - RootBeanDefinition interceptorDef = new RootBeanDefinition( SpringStepMethodInterceptor.class ); - interceptorDef.setScope( AbstractBeanDefinition.SCOPE_PROTOTYPE ); - interceptorDef.setSource( eleSource ); - interceptorDef.setRole( BeanDefinition.ROLE_INFRASTRUCTURE ); - String interceptorName = parserContext.getReaderContext().registerWithGeneratedName( interceptorDef ); - logger.debug( "Registered SpringStepMethodInterceptor with name " + interceptorName ); - - // create Scenario Executor - RootBeanDefinition executorDef = new RootBeanDefinition( SpringStageCreator.class ); - executorDef.setScope( AbstractBeanDefinition.SCOPE_PROTOTYPE ); - executorDef.setSource( eleSource ); - interceptorDef.setRole( BeanDefinition.ROLE_INFRASTRUCTURE ); - String executorName = parserContext.getReaderContext().registerWithGeneratedName( executorDef ); + RootBeanDefinition stageCreator = new RootBeanDefinition( SpringStageCreator.class ); + stageCreator.setSource( eleSource ); + stageCreator.setRole( BeanDefinition.ROLE_INFRASTRUCTURE ); + String executorName = parserContext.getReaderContext().registerWithGeneratedName( stageCreator ); logger.debug( "Registered SpringStageCreator with name " + executorName ); - // create AutoProxyCreator - RootBeanDefinition autoProxyCreatorDef = new RootBeanDefinition( JGivenStageAutoProxyCreator.class ); - autoProxyCreatorDef.setRole( BeanDefinition.ROLE_INFRASTRUCTURE ); - parserContext.getRegistry().registerBeanDefinition( BEAN_NAME, autoProxyCreatorDef ); + RootBeanDefinition beanFactoryPostProcessor = new RootBeanDefinition( JGivenBeanFactoryPostProcessor.class ); + beanFactoryPostProcessor.setRole( BeanDefinition.ROLE_INFRASTRUCTURE ); + parserContext.getRegistry().registerBeanDefinition( BEAN_NAME, beanFactoryPostProcessor ); CompositeComponentDefinition componentDefinition = new CompositeComponentDefinition( element.getTagName(), eleSource ); - componentDefinition.addNestedComponent( new BeanComponentDefinition( interceptorDef, interceptorName ) ); - componentDefinition.addNestedComponent( new BeanComponentDefinition( executorDef, executorName ) ); - componentDefinition.addNestedComponent( new BeanComponentDefinition( autoProxyCreatorDef, BEAN_NAME ) ); + componentDefinition.addNestedComponent( new BeanComponentDefinition( stageCreator, executorName ) ); + componentDefinition.addNestedComponent( new BeanComponentDefinition( beanFactoryPostProcessor, BEAN_NAME ) ); parserContext.registerComponent( componentDefinition ); } return null; diff --git a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/JGivenBeanFactoryPostProcessor.java b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/JGivenBeanFactoryPostProcessor.java new file mode 100644 index 00000000000..b4b10332835 --- /dev/null +++ b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/JGivenBeanFactoryPostProcessor.java @@ -0,0 +1,46 @@ +package com.tngtech.jgiven.integration.spring; + +import com.tngtech.jgiven.impl.ByteBuddyStageCreator; +import org.springframework.beans.BeansException; +import org.springframework.beans.FatalBeanException; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; + +import java.util.Iterator; + + +public class JGivenBeanFactoryPostProcessor implements BeanFactoryPostProcessor { + + private final ByteBuddyStageCreator buddyStageCreator = new ByteBuddyStageCreator(); + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + Iterator beanNames = beanFactory.getBeanNamesIterator(); + while (beanNames.hasNext()) { + String beanName = beanNames.next(); + if (beanFactory.containsBeanDefinition(beanName)) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); + if (beanDefinition instanceof AnnotatedBeanDefinition) { + AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition; + if (annotatedBeanDefinition.getMetadata().hasAnnotation(JGivenStage.class.getName())) { + String className = beanDefinition.getBeanClassName(); + Class stageClass = createStageClass(beanName, className); + beanDefinition.setBeanClassName(stageClass.getName()); + } + } + } + + } + } + + private Class createStageClass(String beanName, String className) { + try { + Class aClass = Thread.currentThread().getContextClassLoader().loadClass(className); + return buddyStageCreator.createStageClass(aClass, null); + } catch (ClassNotFoundException e) { + throw new FatalBeanException("Error while trying to create JGiven stage for bean "+beanName, e); + } + } +} diff --git a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/JGivenSpringConfiguration.java b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/JGivenSpringConfiguration.java index 8d3744e016b..ce711e3c446 100644 --- a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/JGivenSpringConfiguration.java +++ b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/JGivenSpringConfiguration.java @@ -1,10 +1,7 @@ package com.tngtech.jgiven.integration.spring; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Role; -import org.springframework.context.annotation.Scope; /** * {@code @Configuration} class that registers all beans an post-processors @@ -16,24 +13,13 @@ public class JGivenSpringConfiguration { @Bean - @Scope( "prototype" ) - public SpringStepMethodInterceptor springStepMethodInterceptor() { - return new SpringStepMethodInterceptor(); - } - - @Bean - @Scope( "prototype" ) public SpringStageCreator springStageCreator() { return new SpringStageCreator(); } - /* - * configure support for {@link JGivenStage} annotation - */ @Bean - @Role( BeanDefinition.ROLE_INFRASTRUCTURE ) - public JGivenStageAutoProxyCreator jGivenStageAutoProxyCreator() { - return new JGivenStageAutoProxyCreator(); + public JGivenBeanFactoryPostProcessor jGivenPostBeanProcessor() { + return new JGivenBeanFactoryPostProcessor(); } } diff --git a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/JGivenStageAutoProxyCreator.java b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/JGivenStageAutoProxyCreator.java deleted file mode 100644 index 0d2d14b5103..00000000000 --- a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/JGivenStageAutoProxyCreator.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.tngtech.jgiven.integration.spring; - -import org.springframework.aop.TargetSource; -import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator; -import org.springframework.beans.BeansException; - -/** - * AutoProxyCreator that creates JGiven advices for all beans that - * are annotated with the {@link JGivenStage} annotation. See below - * on how to configure this bean. - * - *

- * Sample configuration:
- *

- *   {@literal @}Bean
- *   public JGivenStageAutoProxyCreator jGivenStageAutoProxyCreator() {
- *       return new JGivenStageAutoProxyCreator();
- *   }
- *
- * 
- * @since 0.8.0 - */ -public class JGivenStageAutoProxyCreator extends AbstractAutoProxyCreator { - - private static final long serialVersionUID = 1L; - - @Override - protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, - String beanName, TargetSource customTargetSource) - throws BeansException { - if (beanClass.isAnnotationPresent(JGivenStage.class)) { - return new Object[] { getBeanFactory().getBean(SpringStepMethodInterceptor.class) }; - } - return DO_NOT_PROXY; - } - -} diff --git a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringScenarioTest.java b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringScenarioTest.java index 5e9d4dad7da..0928c22ac8b 100644 --- a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringScenarioTest.java +++ b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringScenarioTest.java @@ -20,6 +20,7 @@ public class SpringScenarioTest extends ScenarioTest implements BeanFactoryAware { + @Override public void setBeanFactory( BeanFactory beanFactory ) { getScenario().setStageCreator( beanFactory.getBean( SpringStageCreator.class ) ); } diff --git a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringStageCreator.java b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringStageCreator.java index 98574937667..f788309f56f 100644 --- a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringStageCreator.java +++ b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringStageCreator.java @@ -2,14 +2,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.aop.Advisor; -import org.springframework.aop.framework.Advised; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.util.ClassUtils; import com.tngtech.jgiven.impl.ByteBuddyStageCreator; +import com.tngtech.jgiven.impl.intercept.StageInterceptorInternal; import com.tngtech.jgiven.impl.intercept.StepInterceptor; /** @@ -36,25 +35,23 @@ public class SpringStageCreator extends ByteBuddyStageCreator { private ApplicationContext applicationContext; @Override - public T createStage(Class stepsClass, StepInterceptor stepInterceptor ) { + public T createStage( Class stageClass, StepInterceptor stepInterceptor ) { try { - T bean = applicationContext.getBean( stepsClass ); - Advised advised = (Advised) bean; - Advisor[] advisors = advised.getAdvisors(); - for( Advisor advisor : advisors ) { - if( advisor.getAdvice() instanceof SpringStepMethodInterceptor ) { - SpringStepMethodInterceptor interceptor = (SpringStepMethodInterceptor) advisor.getAdvice(); - interceptor.setStepInterceptor(stepInterceptor); - } - } + T bean = applicationContext.getBean( stageClass ); + ( (StageInterceptorInternal) bean ).setStepInterceptor( stepInterceptor ); return bean; } catch( NoSuchBeanDefinitionException nbe ) { - return super.createStage( stepsClass, stepInterceptor ); + return super.createStage( stageClass, stepInterceptor ); } catch( ClassCastException cce ) { - log.warn( "Class " + ClassUtils.getShortName( stepsClass ) - + " is not advised with SpringStepMethodInterceptor. Falling back to cglib based proxy, strange things may happen." ); - return super.createStage( stepsClass, stepInterceptor ); + log.warn( "Class " + ClassUtils.getShortName( stageClass ) + + " is not annotated with @JGivenStage. Falling back to default JGiven proxy. Spring features will not be supported for this stage instance.", + cce ); + return super.createStage( stageClass, stepInterceptor ); + } catch( Exception e ) { + log.error( "Error while trying to get the Spring bean for stage class "+stageClass, e ); + return null; } + } } diff --git a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringStepMethodInterceptor.java b/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringStepMethodInterceptor.java deleted file mode 100644 index 8c0e30b02a4..00000000000 --- a/jgiven-spring/src/main/java/com/tngtech/jgiven/integration/spring/SpringStepMethodInterceptor.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.tngtech.jgiven.integration.spring; - -import java.lang.reflect.Method; - -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; - -import com.tngtech.jgiven.impl.intercept.StepInterceptor; -import com.tngtech.jgiven.impl.intercept.StepInterceptor.Invoker; - -/** - * StepInterceptorImpl that uses {@link MethodInterceptor} for intercepting JGiven methods - * See below on how to configure this bean. - * - *

- * Sample Configuration: - *

- * {@literal @}Bean
- * {@literal @}Scope("prototype")
- * public SpringStepMethodInterceptor springStepMethodInterceptor() {
- *     return new SpringStepMethodInterceptor();
- * }
- * 
- *

- * The StepInterceptorImpl is stateful, and thus should use "prototype" scope - * @since 0.8.0 - */ -public class SpringStepMethodInterceptor implements MethodInterceptor { - - private StepInterceptor stepInterceptor; - - @Override - public Object invoke( final MethodInvocation invocation ) throws Throwable { - Object receiver = invocation.getThis(); - Method method = invocation.getMethod(); - Object[] parameters = invocation.getArguments(); - Invoker invoker = new Invoker() { - - @Override - public Object proceed() throws Throwable { - return invocation.proceed(); - } - }; - return stepInterceptor.intercept( receiver, method, parameters, invoker ); - } - - public void setStepInterceptor(StepInterceptor stepInterceptor) { - this.stepInterceptor = stepInterceptor; - } -} diff --git a/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/config/TestSpringConfig.java b/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/config/TestSpringConfig.java index e0fd3d78929..9044f62490d 100644 --- a/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/config/TestSpringConfig.java +++ b/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/config/TestSpringConfig.java @@ -1,7 +1,5 @@ package com.tngtech.jgiven.integration.spring.config; -import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -10,21 +8,21 @@ @Configuration // auto configuration for JGiven @EnableJGiven -@ComponentScan(basePackages = "com.tngtech.jgiven.integration.spring.test") +@ComponentScan( basePackages = "com.tngtech.jgiven.integration.spring.test" ) public class TestSpringConfig { - /* * example for non-invasive usage of the {@link SpringStepMethodInterceptor} * @return BeanNameAutoProxyCreator that proxies regular spring beans */ + /* @Bean public BeanNameAutoProxyCreator jGivenBeanNameAutoProxyCreator() { BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator(); - beanNameAutoProxyCreator.setBeanNames(new String[]{"simpleTestSpringSteps"}); - beanNameAutoProxyCreator.setInterceptorNames(new String[]{"springStepMethodInterceptor"}); + beanNameAutoProxyCreator.setBeanNames( new String[] { "simpleTestSpringSteps" } ); + beanNameAutoProxyCreator.setInterceptorNames( new String[] { "springStepMethodInterceptor" } ); return beanNameAutoProxyCreator; } - + */ } diff --git a/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/test/SimpleTestSpringSteps.java b/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/test/SimpleTestSpringSteps.java index ef13ddba417..f2ac003edd5 100644 --- a/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/test/SimpleTestSpringSteps.java +++ b/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/test/SimpleTestSpringSteps.java @@ -2,8 +2,8 @@ import org.assertj.core.api.Assertions; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import com.tngtech.jgiven.annotation.NestedSteps; import com.tngtech.jgiven.integration.spring.JGivenStage; import com.tngtech.jgiven.integration.spring.config.TestSpringConfig; @@ -18,8 +18,9 @@ * See {@link TestSpringConfig#jGivenBeanNameAutoProxyCreator()} on how to setup such beans. * */ -@Component -class SimpleTestSpringSteps { +//@Component +@JGivenStage +class SimpleTestSpringSteps { @Autowired TestBean testBean; @@ -32,12 +33,18 @@ public SimpleTestSpringSteps methods_on_this_component_are_called() { return this; } - public SimpleTestSpringSteps method_with_parameter_is_called(String message) { - testBean.sayHello(message); + public SimpleTestSpringSteps method_with_parameter_is_called( String message ) { + testBean.sayHello( message ); return this; } public void beans_are_injected() { Assertions.assertThat( testBean ).isNotNull(); } + + @NestedSteps + public void a_nested_step() { + this.beans_are_injected(); + this.methods_on_this_component_are_called(); + } } \ No newline at end of file diff --git a/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/test/SpringScenarioTestTest.java b/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/test/SpringScenarioTestTest.java index 6782dab8c2a..d897adc4bb1 100644 --- a/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/test/SpringScenarioTestTest.java +++ b/jgiven-spring/src/test/java/com/tngtech/jgiven/integration/spring/test/SpringScenarioTestTest.java @@ -1,5 +1,7 @@ package com.tngtech.jgiven.integration.spring.test; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; @@ -18,4 +20,14 @@ public void spring_can_inject_beans_into_stages() { when().methods_on_this_component_are_called(); then().beans_are_injected(); } + + @Test + public void nested_steps_work_with_Spring() throws Throwable { + given().a_nested_step(); + + getScenario().finished(); + + assertThat( getScenario().getModel().getFirstStepModelOfLastScenario().getNestedSteps() ).hasSize( 2 ); + } + }