I try to add Cucumber to test my Java project, I wrote a feature file:
Feature: I visit the website
Scenario: I read the home page
Given I am on the "home" page
I wrote a class to run the cukes
import cucumber.api.CucumberOptions;
/**
* See https://cucumber.io/docs/reference/jvm#junit-runner
*/
@RunWith(Cucumber.class)
@CucumberOptions(glue = {"classpath:stepdefinitions"},features = {"src/test/features"})
public class RunCukesTest {
}
And the step definition: (I've the feeling that I'm missing annotations here)
package com.myproject.test.stepdefinitions;
import cucumber.api.PendingException;
import cucumber.api.java8.En;
public class WebSteps implements En {
public WebSteps() {
Given("^I am on the \"([^\"]*)\" page$", (String page) -> {
System.out.println(page);
});
}
}
So whatever I put in glue = {}
, the step is not found. With glue = {"classpath:stepdefinitions"}
or glue = {"stepdefinitions"}
the test is green and I have the "You can implement missing steps with the snippets below" message (the step is not found). If I put the full package path, either glue = {"com.myproject.test.stepdefinitions"}
or glue = {"classpath:com.myproject.test.stepdefinitions"}
I have a stack trace:
java.lang.NoClassDefFoundError: org/springframework/test/context/ContextHierarchy
at cucumber.runtime.java.spring.SpringFactory.annotatedWithSupportedSpringRootTestAnnotations(SpringFactory.java:198)
at cucumber.runtime.java.spring.SpringFactory.dependsOnSpringContext(SpringFactory.java:187)
at cucumber.runtime.java.spring.SpringFactory.addClass(SpringFactory.java:61)
at cucumber.runtime.java.JavaBackend.loadGlue(JavaBackend.java:96)
at cucumber.runtime.Runtime.<init>(Runtime.java:91)
at cucumber.runtime.Runtime.<init>(Runtime.java:69)
at cucumber.runtime.Runtime.<init>(Runtime.java:65)
at cucumber.api.junit.Cucumber.createRuntime(Cucumber.java:78)
at cucumber.api.junit.Cucumber.<init>(Cucumber.java:58)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:29)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:21)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:26)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createUnfilteredTest(JUnit4TestLoader.java:84)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:70)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:43)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:444)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.ClassNotFoundException: org.springframework.test.context.ContextHierarchy
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 26 more
Here is what I added in my pom.xml
:
<!-- Tests with cucumber -->
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java8</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<!-- Dependency Injection with Spring https://cucumber.io/docs/reference/java-di#spring -->
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-spring</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<!-- JUnit Runner : https://cucumber.io/docs/reference/jvm#running -->
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
My project uses Spring 4.
You are not missing an annotation. You are missing content in your classpath.
I would start with simplifying the setup bit. You are using Maven and therefore I would place the sample feature file in the same package as the RunCukesTest class. If RunCukesTest lives in the package com.myproject.test, then would this mean:
src/test/resources/com/myproject/test
I think you currently are using src/test/features for your feature files. That is not a path that is included in your classpath by Maven. Not unless you explicit adds it, which you don't show us if you do or not.
Moving the feature file should allow you to remove the @CucumberOptions and remove one potential source of problems.
I would then implement the step in a class in the same package as RunCukesTest. I would use the snippets provided when you run your build, mvn test. And finally do the system.out.println() you have to really know that the expected method indeed is executed. The snippets are probably the pre Java 8 way of doing things.
With this working, I would try to get the Java 8 steps working.
More details can be seen in the source code for Cucumber: https://github.com/cucumber/cucumber-jvm/tree/master/java8/src/test/java/cucumber/runtime/java8/test