Skip to content

Commit fb5c95b

Browse files
committed
Detect subclassing in glue code and report to the user that it's illegal. Closes #301
1 parent e1e4a5e commit fb5c95b

File tree

8 files changed

+58
-11
lines changed

8 files changed

+58
-11
lines changed

History.md

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

3+
* [Java] Detect subclassing in glue code and report to the user that it's illegal. ([#301](https://github.com/cucumber/cucumber-jvm/issues/301) Aslak Hellesøy)
34
* [Core] Friendlier error message when XStream fails to assign null to primitive fields ([#296](https://github.com/cucumber/cucumber-jvm/issues/296) Aslak Hellesøy)
45

56
## [1.0.3](https://github.com/cucumber/cucumber-jvm/compare/v1.0.2...1.0.3)

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

+9-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public void scan(JavaBackend javaBackend, List<String> gluePaths) {
4141
}
4242
if (glueCodeClass != null) {
4343
for (Method method : glueCodeClass.getMethods()) {
44-
scan(javaBackend, method);
44+
scan(javaBackend, method, glueCodeClass);
4545
}
4646
}
4747
}
@@ -53,11 +53,18 @@ public void scan(JavaBackend javaBackend, List<String> gluePaths) {
5353
*
5454
* @param javaBackend the backend where stepdefs and hooks will be registered
5555
* @param method a candidate for being a stepdef or hook
56+
* @param glueCodeClass
5657
*/
57-
public void scan(JavaBackend javaBackend, Method method) {
58+
public void scan(JavaBackend javaBackend, Method method, Class<?> glueCodeClass) {
5859
for (Class<? extends Annotation> cucumberAnnotationClass : cucumberAnnotationClasses) {
5960
Annotation annotation = method.getAnnotation(cucumberAnnotationClass);
6061
if (annotation != null && !annotation.annotationType().equals(Order.class)) {
62+
if(!method.getDeclaringClass().isAssignableFrom(glueCodeClass)) {
63+
throw new CucumberException(String.format("%s isn't assignable from %s", method.getDeclaringClass(), glueCodeClass));
64+
}
65+
if(!glueCodeClass.equals(method.getDeclaringClass())) {
66+
throw new CucumberException(String.format("You're not allowed to extend classes that define Step Definitions or hooks. %s extends %s", glueCodeClass, method.getDeclaringClass()));
67+
}
6168
if (isHookAnnotation(annotation)) {
6269
javaBackend.addHook(annotation, method);
6370
} else if (isStepdefAnnotation(annotation)) {

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,11 @@ public void loadGlue(Glue glue, List<String> gluePaths) {
6464
*
6565
* @param glue where stepdefs and hooks will be added.
6666
* @param method a candidate method.
67+
* @param glueCodeClass the class implementing the method. Must not be a subclass of the class implementing the method.
6768
*/
68-
public void loadGlue(Glue glue, Method method) {
69+
public void loadGlue(Glue glue, Method method, Class<?> glueCodeClass) {
6970
this.glue = glue;
70-
classpathMethodScanner.scan(this, method);
71+
classpathMethodScanner.scan(this, method, glueCodeClass);
7172
}
7273

7374
@Override

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

+26-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
import cucumber.annotation.Before;
44
import cucumber.io.ClasspathResourceLoader;
5+
import cucumber.runtime.CucumberException;
56
import cucumber.runtime.Glue;
67
import org.junit.Test;
78
import org.mockito.Mockito;
89
import org.mockito.internal.util.reflection.Whitebox;
910

11+
import static org.junit.Assert.assertEquals;
12+
import static org.junit.Assert.fail;
1013
import static org.mockito.Mockito.times;
1114
import static org.mockito.Mockito.verify;
1215
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -15,7 +18,6 @@ public class ClasspathMethodScannerTest {
1518

1619
@Test
1720
public void loadGlue_registers_the_methods_declaring_class_in_the_object_factory() throws NoSuchMethodException {
18-
1921
ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader(Thread.currentThread().getContextClassLoader());
2022
ClasspathMethodScanner classpathMethodScanner = new ClasspathMethodScanner(resourceLoader);
2123

@@ -25,12 +27,34 @@ public void loadGlue_registers_the_methods_declaring_class_in_the_object_factory
2527
Whitebox.setInternalState(backend, "glue", world);
2628

2729
// this delegates to classpathMethodScanner.scan which we test
28-
classpathMethodScanner.scan(backend, BaseStepDefs.class.getMethod("m"));
30+
classpathMethodScanner.scan(backend, BaseStepDefs.class.getMethod("m"), BaseStepDefs.class);
2931

3032
verify(factory, times(1)).addClass(BaseStepDefs.class);
3133
verifyNoMoreInteractions(factory);
3234
}
3335

36+
@Test
37+
public void loadGlue_fails_when_class_is_not_method_declaring_class() throws NoSuchMethodException {
38+
JavaBackend backend = new JavaBackend((ObjectFactory) null);
39+
try {
40+
backend.loadGlue(null, BaseStepDefs.class.getMethod("m"), Stepdefs2.class);
41+
fail();
42+
} catch (CucumberException e) {
43+
assertEquals("You're not allowed to extend classes that define Step Definitions or hooks. class cucumber.runtime.java.ClasspathMethodScannerTest$Stepdefs2 extends class cucumber.runtime.java.ClasspathMethodScannerTest$BaseStepDefs", e.getMessage());
44+
}
45+
}
46+
47+
@Test
48+
public void loadGlue_fails_when_class_is_not_subclass_of_declaring_class() throws NoSuchMethodException {
49+
JavaBackend backend = new JavaBackend((ObjectFactory) null);
50+
try {
51+
backend.loadGlue(null, BaseStepDefs.class.getMethod("m"), String.class);
52+
fail();
53+
} catch (CucumberException e) {
54+
assertEquals("class cucumber.runtime.java.ClasspathMethodScannerTest$BaseStepDefs isn't assignable from class java.lang.String", e.getMessage());
55+
}
56+
}
57+
3458
public static class Stepdefs2 extends BaseStepDefs {
3559
public interface Interface1 {
3660
}

java/src/test/java/cucumber/runtime/java/JavaBackendTest.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
import cucumber.runtime.HookDefinition;
77
import cucumber.runtime.StepDefinition;
88
import cucumber.runtime.StepDefinitionMatch;
9-
import cucumber.runtime.java.test.Stepdefs;
9+
import cucumber.runtime.java.incorrectlysubclassedstepdefs.SubclassesStepdefs;
10+
import cucumber.runtime.java.stepdefs.Stepdefs;
1011
import gherkin.I18n;
1112
import gherkin.formatter.model.Step;
1213
import org.junit.Test;
@@ -25,19 +26,27 @@ public void doesnt_like_path_like_glue() {
2526
ObjectFactory factory = new DefaultJavaObjectFactory();
2627
JavaBackend backend = new JavaBackend(factory);
2728
GlueStub world = new GlueStub();
28-
backend.loadGlue(world, asList("cucumber/runtime/java/test"));
29+
backend.loadGlue(world, asList("cucumber/runtime/java/stepdefs"));
2930
}
3031

3132
@Test
3233
public void finds_step_definitions_by_scanning_for_annotations() {
3334
ObjectFactory factory = new DefaultJavaObjectFactory();
3435
JavaBackend backend = new JavaBackend(factory);
3536
GlueStub world = new GlueStub();
36-
backend.loadGlue(world, asList("cucumber.runtime.java.test"));
37+
backend.loadGlue(world, asList("cucumber.runtime.java.stepdefs"));
3738
backend.buildWorld();
3839
assertEquals(Stepdefs.class, factory.getInstance(Stepdefs.class).getClass());
3940
}
4041

42+
@Test(expected = CucumberException.class)
43+
public void detects_subclassed_glue_and_throws_exception() {
44+
ObjectFactory factory = new DefaultJavaObjectFactory();
45+
JavaBackend backend = new JavaBackend(factory);
46+
GlueStub world = new GlueStub();
47+
backend.loadGlue(world, asList("cucumber.runtime.java.stepdefs", "cucumber.runtime.java.incorrectlysubclassedstepdefs"));
48+
}
49+
4150
private class GlueStub implements Glue {
4251
public final List<StepDefinition> stepDefinitions = new ArrayList<StepDefinition>();
4352

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

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131

3232
public class JavaStepDefinitionTest {
3333
private static final List<Comment> NO_COMMENTS = Collections.emptyList();
34-
private static final List<String> NO_PATHS = Collections.emptyList();
3534
private static final Method THREE_DISABLED_MICE;
3635
private static final Method THREE_BLIND_ANIMALS;
3736
private static final I18n ENGLISH = new I18n("en");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package cucumber.runtime.java.incorrectlysubclassedstepdefs;
2+
3+
import cucumber.runtime.java.stepdefs.Stepdefs;
4+
5+
public class SubclassesStepdefs extends Stepdefs {
6+
}

java/src/test/java/cucumber/runtime/java/test/Stepdefs.java renamed to java/src/test/java/cucumber/runtime/java/stepdefs/Stepdefs.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package cucumber.runtime.java.test;
1+
package cucumber.runtime.java.stepdefs;
22

33
import cucumber.annotation.en.Given;
44

0 commit comments

Comments
 (0)