Skip to content

Commit f5b7230

Browse files
committed
[Spring] Do not share ContextCache between instances of SpringFactory
The TestContextManager(Class<?>) uses a static ContextCache. This meant that all step definitions from all tests would be registered in same context, possibly multiple times when running in parallel. This is resolved by creating a ThreadLocal ContextCache for each SpringFactory. Related issues: - https://travis-ci.org/cucumber/cucumber-jvm/jobs/244215644 - #1148
1 parent c70c0ec commit f5b7230

File tree

2 files changed

+45
-14
lines changed

2 files changed

+45
-14
lines changed

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

+20-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package cucumber.runtime.java.spring;
22

3-
import cucumber.runtime.CucumberException;
3+
import static org.springframework.test.context.FixBootstrapUtils.createBootstrapContext;
4+
import static org.springframework.test.context.FixBootstrapUtils.resolveTestContextBootstrapper;
5+
46
import cucumber.api.java.ObjectFactory;
7+
import cucumber.runtime.CucumberException;
58
import org.springframework.beans.BeansException;
69
import org.springframework.beans.factory.config.BeanDefinition;
710
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -75,20 +78,20 @@ private void checkAnnotationsEqual(Class<?> stepClassWithSpringContext, Class<?>
7578
Annotation[] annotations2 = stepClass.getAnnotations();
7679
if (annotations1.length != annotations2.length) {
7780
throw new CucumberException("Annotations differs on glue classes found: " +
78-
stepClassWithSpringContext.getName() + ", " +
79-
stepClass.getName());
81+
stepClassWithSpringContext.getName() + ", " +
82+
stepClass.getName());
8083
}
8184
for (Annotation annotation : annotations1) {
8285
if (!isAnnotationInArray(annotation, annotations2)) {
8386
throw new CucumberException("Annotations differs on glue classes found: " +
84-
stepClassWithSpringContext.getName() + ", " +
85-
stepClass.getName());
87+
stepClassWithSpringContext.getName() + ", " +
88+
stepClass.getName());
8689
}
8790
}
8891
}
8992

9093
private boolean isAnnotationInArray(Annotation annotation, Annotation[] annotations) {
91-
for (Annotation annotationFromArray: annotations) {
94+
for (Annotation annotationFromArray : annotations) {
9295
if (annotation.equals(annotationFromArray)) {
9396
return true;
9497
}
@@ -152,9 +155,9 @@ private boolean isNewContextCreated() {
152155
private void registerStepClassBeanDefinition(ConfigurableListableBeanFactory beanFactory, Class<?> stepClass) {
153156
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
154157
BeanDefinition beanDefinition = BeanDefinitionBuilder
155-
.genericBeanDefinition(stepClass)
156-
.setScope(GlueCodeScope.NAME)
157-
.getBeanDefinition();
158+
.genericBeanDefinition(stepClass)
159+
.setScope(GlueCodeScope.NAME)
160+
.getBeanDefinition();
158161
registry.registerBeanDefinition(stepClass.getName(), beanDefinition);
159162
}
160163

@@ -200,25 +203,28 @@ private boolean annotatedWithSupportedSpringRootTestAnnotations(Class<?> type) {
200203
}
201204
}
202205

206+
203207
class CucumberTestContextManager extends TestContextManager {
204208

205-
public CucumberTestContextManager(Class<?> testClass) {
206-
super(testClass);
209+
CucumberTestContextManager(Class<?> testClass) {
210+
// Does the same as TestContextManager(Class<?>) but creates a
211+
// DefaultCacheAwareContextLoaderDelegate that does not use a static contextCache.
212+
super(resolveTestContextBootstrapper(createBootstrapContext(testClass)));
207213
registerGlueCodeScope(getContext());
208214
}
209215

210-
public ConfigurableListableBeanFactory getBeanFactory() {
216+
ConfigurableListableBeanFactory getBeanFactory() {
211217
return getContext().getBeanFactory();
212218
}
213219

214220
private ConfigurableApplicationContext getContext() {
215-
return (ConfigurableApplicationContext)getTestContext().getApplicationContext();
221+
return (ConfigurableApplicationContext) getTestContext().getApplicationContext();
216222
}
217223

218224
private void registerGlueCodeScope(ConfigurableApplicationContext context) {
219225
do {
220226
context.getBeanFactory().registerScope(GlueCodeScope.NAME, new GlueCodeScope());
221-
context = (ConfigurableApplicationContext)context.getParent();
227+
context = (ConfigurableApplicationContext) context.getParent();
222228
} while (context != null);
223229
}
224230
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.springframework.test.context;
2+
3+
import org.springframework.test.context.cache.ContextCache;
4+
import org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate;
5+
import org.springframework.test.context.cache.DefaultContextCache;
6+
import org.springframework.test.context.support.DefaultBootstrapContext;
7+
8+
public class FixBootstrapUtils extends BootstrapUtils {
9+
10+
private static ThreadLocal<ContextCache> contextCache = new ThreadLocal<ContextCache>(){
11+
@Override
12+
protected ContextCache initialValue() {
13+
return new DefaultContextCache();
14+
}
15+
};
16+
17+
public static BootstrapContext createBootstrapContext(Class<?> testClass) {
18+
CacheAwareContextLoaderDelegate contextLoader = new DefaultCacheAwareContextLoaderDelegate(contextCache.get());
19+
return new DefaultBootstrapContext(testClass, contextLoader);
20+
}
21+
22+
public static TestContextBootstrapper resolveTestContextBootstrapper(BootstrapContext bootstrapContext) {
23+
return BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
24+
}
25+
}

0 commit comments

Comments
 (0)