Skip to content

Commit 4a718d8

Browse files
aslakhellesoyhutchy2570
authored and
hutchy2570
committed
Don't allow inheritance in glue code (stepdefs and hooks). Also fixes cucumber#257 in a more encapsulated way.
1 parent ff97576 commit 4a718d8

File tree

7 files changed

+58
-37
lines changed

7 files changed

+58
-37
lines changed

History.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## [Git master](https://github.com/cucumber/cucumber-jvm/compare/v1.0.0.RC20...master)
22

3+
* [Java] Open up
4+
* [Java] Inheritance in glue classes (stepdefs and hooks) is no longer supported - it causes too many problems. (Aslak Hellesøy).
35
* [JUnit] `@Cucumber.Options` annotation replaces `@Feature` annotation ([#160](https://github.com/cucumber/cucumber-jvm/issues/160) Aslak Hellesøy)
46
* [Spring] Slow Spring context performance ([#241](https://github.com/cucumber/cucumber-jvm/issues/241), [#242](https://github.com/cucumber/cucumber-jvm/pull/242) Vladimir Klyushnikov)
57
* [Core] Support for java.util.Calendar arguments in stepdefs. (Aslak Hellesøy)

java/src/main/java/cucumber/runtime/java/ClasspathMethodScanner.java

+23-10
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,23 @@
1111
import java.util.Collection;
1212
import java.util.List;
1313

14-
public class ClasspathMethodScanner {
14+
class ClasspathMethodScanner {
1515

1616
private final ClasspathResourceLoader resourceLoader;
17+
private final Collection<Class<? extends Annotation>> cucumberAnnotationClasses;
1718

1819
public ClasspathMethodScanner(ClasspathResourceLoader resourceLoader) {
1920
this.resourceLoader = resourceLoader;
21+
cucumberAnnotationClasses = findCucumberAnnotationClasses();
2022
}
2123

24+
/**
25+
* Registers step definitions and hooks.
26+
*
27+
* @param javaBackend the backend where stepdefs and hooks will be registered
28+
* @param gluePaths where to look
29+
*/
2230
public void scan(JavaBackend javaBackend, List<String> gluePaths) {
23-
Collection<Class<? extends Annotation>> cucumberAnnotationClasses = findCucumberAnnotationClasses();
2431
for (String gluePath : gluePaths) {
2532
String packageName = gluePath.replace('/', '.').replace('\\', '.'); // Sometimes the gluePath will be a path, not a package
2633
for (Class<?> glueCodeClass : resourceLoader.getDescendants(Object.class, packageName)) {
@@ -30,30 +37,36 @@ public void scan(JavaBackend javaBackend, List<String> gluePaths) {
3037
}
3138
if (glueCodeClass != null) {
3239
for (Method method : glueCodeClass.getMethods()) {
33-
scan(glueCodeClass, method, cucumberAnnotationClasses, javaBackend);
40+
scan(javaBackend, method);
3441
}
3542
}
3643
}
3744
}
3845
}
3946

40-
private Collection<Class<? extends Annotation>> findCucumberAnnotationClasses() {
41-
return resourceLoader.getAnnotations("cucumber.annotation");
42-
}
43-
44-
private void scan(Class<?> glueCodeClass, Method method, Collection<Class<? extends Annotation>> cucumberAnnotationClasses, JavaBackend javaBackend) {
47+
/**
48+
* Registers step definitions and hooks.
49+
*
50+
* @param javaBackend the backend where stepdefs and hooks will be registered
51+
* @param method a candidate for being a stepdef or hook
52+
*/
53+
public void scan(JavaBackend javaBackend, Method method) {
4554
for (Class<? extends Annotation> cucumberAnnotationClass : cucumberAnnotationClasses) {
4655
Annotation annotation = method.getAnnotation(cucumberAnnotationClass);
4756
if (annotation != null && !annotation.annotationType().equals(Order.class)) {
4857
if (isHookAnnotation(annotation)) {
49-
javaBackend.addHook(annotation, glueCodeClass, method);
58+
javaBackend.addHook(annotation, method);
5059
} else if (isStepdefAnnotation(annotation)) {
51-
javaBackend.addStepDefinition(annotation, glueCodeClass, method);
60+
javaBackend.addStepDefinition(annotation, method);
5261
}
5362
}
5463
}
5564
}
5665

66+
private Collection<Class<? extends Annotation>> findCucumberAnnotationClasses() {
67+
return resourceLoader.getAnnotations("cucumber.annotation");
68+
}
69+
5770
private boolean isHookAnnotation(Annotation annotation) {
5871
Class<? extends Annotation> annotationClass = annotation.annotationType();
5972
return annotationClass.equals(Before.class) || annotationClass.equals(After.class);

java/src/main/java/cucumber/runtime/java/JavaBackend.java

+16-4
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ public void loadGlue(Glue glue, List<String> gluePaths) {
5858
classpathMethodScanner.scan(this, gluePaths);
5959
}
6060

61+
/**
62+
* Convenience method for frameworks that wish to load glue from methods explicitly (possibly
63+
* found with a different mechanism than Cucumber's built-in classpath scanning).
64+
*
65+
* @param glue where stepdefs and hooks will be added.
66+
* @param method a candidate method.
67+
*/
68+
public void loadGlue(Glue glue, Method method) {
69+
this.glue = glue;
70+
classpathMethodScanner.scan(this, method);
71+
}
72+
6173
@Override
6274
public void setUnreportedStepExecutor(UnreportedStepExecutor executor) {
6375
//Not used here yet
@@ -78,13 +90,13 @@ public String getSnippet(Step step) {
7890
return snippetGenerator.getSnippet(step);
7991
}
8092

81-
void addStepDefinition(Annotation annotation, Class<?> glueCodeClass, Method method) {
93+
void addStepDefinition(Annotation annotation, Method method) {
8294
try {
8395
Method regexpMethod = annotation.getClass().getMethod("value");
8496
String regexpString = (String) regexpMethod.invoke(annotation);
8597
if (regexpString != null) {
8698
Pattern pattern = Pattern.compile(regexpString);
87-
objectFactory.addClass(glueCodeClass);
99+
objectFactory.addClass(method.getDeclaringClass());
88100
glue.addStepDefinition(new JavaStepDefinition(method, pattern, objectFactory));
89101
}
90102
} catch (NoSuchMethodException e) {
@@ -96,8 +108,8 @@ void addStepDefinition(Annotation annotation, Class<?> glueCodeClass, Method met
96108
}
97109
}
98110

99-
void addHook(Annotation annotation, Class<?> glueCodeClass, Method method) {
100-
objectFactory.addClass(glueCodeClass);
111+
void addHook(Annotation annotation, Method method) {
112+
objectFactory.addClass(method.getDeclaringClass());
101113

102114
Order order = method.getAnnotation(Order.class);
103115
int hookOrder = (order == null) ? Integer.MAX_VALUE : order.value();

java/src/test/java/cucumber/runtime/java/ClasspathMethodScannerTest.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,18 @@
33
import cucumber.annotation.Before;
44
import cucumber.io.ClasspathResourceLoader;
55
import cucumber.runtime.Glue;
6-
import cucumber.runtime.java.test2.Stepdefs2;
76
import org.junit.Test;
87
import org.mockito.Mockito;
98
import org.mockito.internal.util.reflection.Whitebox;
109

11-
import static java.util.Arrays.asList;
1210
import static org.mockito.Mockito.times;
1311
import static org.mockito.Mockito.verify;
1412
import static org.mockito.Mockito.verifyNoMoreInteractions;
1513

1614
public class ClasspathMethodScannerTest {
1715

1816
@Test
19-
public void loadGlue_should_not_try_to_instantiate_super_classes() {
17+
public void loadGlue_registers_the_methods_declaring_class_in_the_object_factory() throws NoSuchMethodException {
2018

2119
ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader(Thread.currentThread().getContextClassLoader());
2220
ClasspathMethodScanner classpathMethodScanner = new ClasspathMethodScanner(resourceLoader);
@@ -27,14 +25,18 @@ public void loadGlue_should_not_try_to_instantiate_super_classes() {
2725
Whitebox.setInternalState(backend, "glue", world);
2826

2927
// this delegates to classpathMethodScanner.scan which we test
30-
classpathMethodScanner.scan(backend, asList("cucumber/runtime/java/test2"));
28+
classpathMethodScanner.scan(backend, BaseStepDefs.class.getMethod("m"));
3129

32-
verify(factory, times(1)).addClass(Stepdefs2.class);
30+
verify(factory, times(1)).addClass(BaseStepDefs.class);
3331
verifyNoMoreInteractions(factory);
3432
}
3533

36-
public static class BaseStepDefs {
34+
public static class Stepdefs2 extends BaseStepDefs {
35+
public interface Interface1 {
36+
}
37+
}
3738

39+
public static class BaseStepDefs {
3840
@Before
3941
public void m() {
4042
}

java/src/test/java/cucumber/runtime/java/JavaHookTest.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public void loadNoGlue() {
4545
@Test
4646
public void before_hooks_get_registered() throws Exception {
4747
backend.buildWorld();
48-
backend.addHook(BEFORE.getAnnotation(Before.class), HasHooks.class, BEFORE);
48+
backend.addHook(BEFORE.getAnnotation(Before.class), BEFORE);
4949
JavaHookDefinition hookDef = (JavaHookDefinition) glue.getBeforeHooks().get(0);
5050
assertEquals(0, glue.getAfterHooks().size());
5151
assertEquals(BEFORE, hookDef.getMethod());
@@ -54,7 +54,7 @@ public void before_hooks_get_registered() throws Exception {
5454
@Test
5555
public void after_hooks_get_registered() throws Exception {
5656
backend.buildWorld();
57-
backend.addHook(AFTER.getAnnotation(After.class), HasHooks.class, AFTER);
57+
backend.addHook(AFTER.getAnnotation(After.class), AFTER);
5858
JavaHookDefinition hookDef = (JavaHookDefinition) glue.getAfterHooks().get(0);
5959
assertEquals(0, glue.getBeforeHooks().size());
6060
assertEquals(AFTER, hookDef.getMethod());
@@ -63,31 +63,31 @@ public void after_hooks_get_registered() throws Exception {
6363
@Test
6464
public void hook_order_gets_registered() {
6565
backend.buildWorld();
66-
backend.addHook(AFTER.getAnnotation(After.class), HasHooks.class, AFTER);
66+
backend.addHook(AFTER.getAnnotation(After.class), AFTER);
6767
HookDefinition hookDef = glue.getAfterHooks().get(0);
6868
assertEquals(1, hookDef.getOrder());
6969
}
7070

7171
@Test
7272
public void hook_with_no_order_is_last() {
7373
backend.buildWorld();
74-
backend.addHook(BEFORE.getAnnotation(Before.class), HasHooks.class, BEFORE);
74+
backend.addHook(BEFORE.getAnnotation(Before.class), BEFORE);
7575
HookDefinition hookDef = glue.getBeforeHooks().get(0);
7676
assertEquals(Integer.MAX_VALUE, hookDef.getOrder());
7777
}
7878

7979
@Test
8080
public void matches_matching_tags() {
8181
backend.buildWorld();
82-
backend.addHook(BEFORE.getAnnotation(Before.class), HasHooks.class, BEFORE);
82+
backend.addHook(BEFORE.getAnnotation(Before.class), BEFORE);
8383
HookDefinition before = glue.getBeforeHooks().get(0);
8484
assertTrue(before.matches(asList(new Tag("@bar", 0), new Tag("@zap", 0))));
8585
}
8686

8787
@Test
8888
public void does_not_match_non_matching_tags() {
8989
backend.buildWorld();
90-
backend.addHook(BEFORE.getAnnotation(Before.class), HasHooks.class, BEFORE);
90+
backend.addHook(BEFORE.getAnnotation(Before.class), BEFORE);
9191
HookDefinition before = glue.getBeforeHooks().get(0);
9292
assertFalse(before.matches(asList(new Tag("@bar", 0))));
9393
}

java/src/test/java/cucumber/runtime/java/JavaStepDefinitionTest.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ public void loadNoGlue() {
5656

5757
@Test
5858
public void throws_ambiguous_when_two_matches_are_found() throws Throwable {
59-
backend.addStepDefinition(THREE_DISABLED_MICE.getAnnotation(Given.class), Defs.class, THREE_DISABLED_MICE);
60-
backend.addStepDefinition(THREE_BLIND_ANIMALS.getAnnotation(Given.class), Defs.class, THREE_BLIND_ANIMALS);
59+
backend.addStepDefinition(THREE_DISABLED_MICE.getAnnotation(Given.class), THREE_DISABLED_MICE);
60+
backend.addStepDefinition(THREE_BLIND_ANIMALS.getAnnotation(Given.class), THREE_BLIND_ANIMALS);
6161

6262
Reporter reporter = mock(Reporter.class);
6363
runtime.buildBackendWorlds(reporter);
@@ -72,7 +72,7 @@ public void throws_ambiguous_when_two_matches_are_found() throws Throwable {
7272

7373
@Test
7474
public void does_not_throw_ambiguous_when_nothing_is_ambiguous() throws Throwable {
75-
backend.addStepDefinition(THREE_DISABLED_MICE.getAnnotation(Given.class), Defs.class, THREE_DISABLED_MICE);
75+
backend.addStepDefinition(THREE_DISABLED_MICE.getAnnotation(Given.class), THREE_DISABLED_MICE);
7676

7777
Reporter reporter = mock(Reporter.class);
7878
runtime.buildBackendWorlds(reporter);

java/src/test/java/cucumber/runtime/java/test2/Stepdefs2.java

-8
This file was deleted.

0 commit comments

Comments
 (0)