Skip to content

Commit 359b825

Browse files
mpkorstanjemlvandijk
authored andcommitted
Support BootstrapWith annotation
Fixes #1242 Fixes #1061 Spring annotations are hierarchical and can be customized. This requires that the entire graph is scanned to determine if one of the supported annotations is present.
1 parent 3ce8634 commit 359b825

File tree

1 file changed

+48
-36
lines changed

1 file changed

+48
-36
lines changed

spring/src/main/java/cucumber/runtime/java/spring/SpringFactory.java

+48-36
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cucumber.runtime.java.spring;
22

3+
import static java.util.Arrays.asList;
34
import static org.springframework.test.context.FixBootstrapUtils.createBootstrapContext;
45
import static org.springframework.test.context.FixBootstrapUtils.resolveTestContextBootstrapper;
56

@@ -14,46 +15,53 @@
1415
import org.springframework.context.support.ClassPathXmlApplicationContext;
1516
import org.springframework.context.support.GenericApplicationContext;
1617
import org.springframework.stereotype.Component;
18+
import org.springframework.test.context.BootstrapWith;
1719
import org.springframework.test.context.ContextConfiguration;
1820
import org.springframework.test.context.ContextHierarchy;
1921
import org.springframework.test.context.TestContextManager;
2022

2123
import java.lang.annotation.Annotation;
2224
import java.util.Collection;
25+
import java.util.Collections;
2326
import java.util.HashSet;
2427
import java.util.Set;
2528
import java.util.Stack;
2629

2730
/**
2831
* Spring based implementation of ObjectFactory.
29-
* <p/>
3032
* <p>
33+
* Application beans are accessible from the step definitions using autowiring
34+
* (with annotations).
35+
* <p>
36+
* SpringFactory uses TestContextManager to manage the spring context. The step definitions are added to the
37+
* TestContextManagers context and the context is reloaded for each scenario.
38+
* <p>
39+
* The spring context can be configured by:
3140
* <ul>
32-
* <li>It uses TestContextManager to manage the spring context.
33-
* Configuration via: @{@link ContextConfiguration} or @{@link ContextHierarchy}
34-
* At least one step definition class needs to have a @ContextConfiguration
35-
* or @ContextHierarchy annotation. If more that one step definition class has such
36-
* an annotation, the annotations must be equal on the different step definition
37-
* classes. If no step definition class with @ContextConfiguration or @ContextHierarchy
38-
* is found, it will try to load cucumber.xml from the classpath.
39-
* </li>
40-
* <li>The step definitions class with @ContextConfiguration or @ContextHierarchy
41-
* annotation, may also have a @{@link org.springframework.test.context.web.WebAppConfiguration}
41+
* <li>Annotating one step definition with: @{@link ContextConfiguration}, @{@link ContextHierarchy}
42+
* or @{@link BootstrapWith}. This step definition can also be annotated
43+
* with @{@link org.springframework.test.context.web.WebAppConfiguration}
4244
* or @{@link org.springframework.test.annotation.DirtiesContext} annotation.
45+
* <p>
46+
* If more that one step definition class has such an annotation, the annotations must be equal on
47+
* the different step definition. <b>Deprecation warning:</b> Annotating multiple step definitions is deprecated.
4348
* </li>
44-
* <li>The step definitions are added to the TestContextManagers context and
45-
* is reloaded for each scenario.</li>
46-
* <li>Step definitions should not be annotated with @{@link Component} or
47-
* other annotations that mark it as eligible for detection by classpath scanning.</li>
48-
* <li>When a step definition class is annotated by @Component or an annotation that has the @Component stereotype an
49-
* exception will be thrown</li>
50-
* </li>
49+
* <li>If no step definition class with @ContextConfiguration or @ContextHierarchy
50+
* is found, it will try to load cucumber.xml from the classpath.</li>
5151
* </ul>
52-
* <p/>
5352
* <p>
54-
* Application beans are accessible from the step definitions using autowiring
55-
* (with annotations).
56-
* </p>
53+
* Notes:
54+
* <ul>
55+
* <li>
56+
* Step definitions should not be annotated with @{@link Component} or other annotations that mark it as eligible for
57+
* detection by classpath scanning. When a step definition class is annotated by @Component or an annotation that has
58+
* the @Component stereotype an exception will be thrown
59+
* </li>
60+
* <li>
61+
* If more that one step definition class is used to configure the spring context, the annotations must be equal
62+
* on the different step definition. Please note that doing so is deprecated.
63+
* </li>
64+
* </ul>
5765
*/
5866
public class SpringFactory implements ObjectFactory {
5967

@@ -85,7 +93,7 @@ public boolean addClass(final Class<?> stepClass) {
8593

8694
private static void checkNoComponentAnnotations(Class<?> type) {
8795
for (Annotation annotation : type.getAnnotations()) {
88-
if (hasComponentStereoType(annotation)) {
96+
if (hasComponentAnnotation(annotation)) {
8997
throw new CucumberException(String.format("" +
9098
"Glue class %1$s was annotated with @%2$s; marking it as a candidate for auto-detection by " +
9199
"Spring. Glue classes are detected and registered by Cucumber. Auto-detection of glue classes by " +
@@ -96,14 +104,18 @@ private static void checkNoComponentAnnotations(Class<?> type) {
96104
}
97105
}
98106

99-
private static boolean hasComponentStereoType(Annotation annotation) {
107+
private static boolean hasComponentAnnotation(Annotation annotation) {
108+
return hasAnnotation(annotation, Collections.<Class<? extends Annotation>>singleton(Component.class));
109+
}
110+
111+
private static boolean hasAnnotation(Annotation annotation, Collection<Class<? extends Annotation>> desired) {
100112
Set<Class<? extends Annotation>> seen = new HashSet<Class<? extends Annotation>>();
101113
Stack<Class<? extends Annotation>> toCheck = new Stack<Class<? extends Annotation>>();
102114
toCheck.add(annotation.annotationType());
103115

104116
while (!toCheck.isEmpty()) {
105117
Class<? extends Annotation> annotationType = toCheck.pop();
106-
if (Component.class.equals(annotationType)) {
118+
if (desired.contains(annotationType)) {
107119
return true;
108120
}
109121

@@ -240,20 +252,20 @@ public <T> T getInstance(final Class<T> type) {
240252
}
241253
}
242254

243-
private boolean dependsOnSpringContext(Class<?> type) {
244-
boolean hasStandardAnnotations = annotatedWithSupportedSpringRootTestAnnotations(type);
245-
246-
if(hasStandardAnnotations) {
247-
return true;
255+
private static boolean dependsOnSpringContext(Class<?> type) {
256+
for (Annotation annotation : type.getAnnotations()) {
257+
if(annotatedWithSupportedSpringRootTestAnnotations(annotation)){
258+
return true;
259+
}
248260
}
249-
250-
final Annotation[] annotations = type.getDeclaredAnnotations();
251-
return (annotations.length == 1) && annotatedWithSupportedSpringRootTestAnnotations(annotations[0].annotationType());
261+
return false;
252262
}
253263

254-
private boolean annotatedWithSupportedSpringRootTestAnnotations(Class<?> type) {
255-
return type.isAnnotationPresent(ContextConfiguration.class)
256-
|| type.isAnnotationPresent(ContextHierarchy.class);
264+
private static boolean annotatedWithSupportedSpringRootTestAnnotations(Annotation type) {
265+
return hasAnnotation(type, asList(
266+
ContextConfiguration.class,
267+
ContextHierarchy.class,
268+
BootstrapWith.class));
257269
}
258270
}
259271

0 commit comments

Comments
 (0)