JBehave throwing NullPointerException when running embeddable stories

1.7k Views Asked by At

I am working on a Spring MVC rest service, for which I am also trying to set up JBehave. This is a Maven project with a single module, and the JBehave code is living under the test scope of that module. My problem is that my sample test (which is just an empty test) is failing with an NPE. I was expecting to see the test pass so that I know JBehave is set up properly. Can you take a look at what I've got and see if you can help me spot what is causing the NPE?

First, relevant parts of my maven configuration:

<properties>
    <jbehave.version>4.0.4</jbehave.version>
</properties>
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.jbehave</groupId>
        <artifactId>jbehave-core</artifactId>
        <version>${jbehave.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.jbehave</groupId>
        <artifactId>jbehave-spring</artifactId>
        <version>${jbehave.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.jbehave.site</groupId>
        <artifactId>jbehave-site-resources</artifactId>
        <version>3.1.1</version>
        <scope>test</scope>
        <type>zip</type>
    </dependency>
    <dependency>
        <groupId>org.jbehave</groupId>
        <artifactId>jbehave-core</artifactId>
        <version>${jbehave.version}</version>
        <classifier>resources</classifier>
        <type>zip</type>
        <scope>test</scope>
    </dependency>
</dependencies>
<build>
    ...    
    <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <scope>test</scope>
                    <testSourceDirectory>${basedir}/src/test/java/</testSourceDirectory>
                    <testClassesDirectory>${project.build.directory}/test-classes/</testClassesDirectory>
                    <includes>
                        <include>com/mycompany/myproject/test/behavior/steps/*.java</include>
                    </includes>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.jbehave</groupId>
                <artifactId>jbehave-maven-plugin</artifactId>
                <version>${jbehave.version}</version>
                <configuration>
                    <scope>test</scope>
                </configuration>
                <executions>
                    <execution>
                        <id>unpack-view-resources</id>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>unpack-view-resources</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>run-stories-as-embeddables</id>
                        <phase>integration-test</phase>
                        <configuration>
                            <scope>test</scope>
                            <includes>
                                <include>com/mycompany/myproject/test/behavior/steps/*.java</include>
                            </includes>
                            <systemProperties>
                                <property>
                                    <name>java.awt.headless</name>
                                    <value>true</value>
                                </property>
                            </systemProperties>
                        </configuration>
                        <goals>
                            <goal>run-stories-as-embeddables</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>log4j</groupId>
                        <artifactId>log4j</artifactId>
                        <version>1.2.17</version>
                        <scope>compile</scope>
                    </dependency>
                </dependencies>
            </plugin>
</build>

I run my tests via mvn clean integration-test -e and I get this output:

$ mvn clean integration-test -e
[INFO] Error stacktraces are turned on.
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building MyProject 0.1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ MyProject ---
[INFO] Deleting C:\Users\mylogin\myproject\target
[INFO]
[INFO] --- jbehave-maven-plugin:4.0.4:unpack-view-resources (unpack-view-resources) @ MyProject ---
[INFO] Unpacked C:\Users\mylogin\.m2\repository\org\jbehave\site\jbehave-site-resources\3.1.1\jbehave-site-resources-3.1.1.zip to C:\Users\mylogin\myproject\target\jbehave\view
[INFO] Unpacked C:\Users\mylogin\.m2\repository\org\jbehave\jbehave-core\4.0.4\jbehave-core-4.0.4-resources.zip to C:\Users\mylogin\myproject\target\jbehave\view
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ MyProject ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 18 resources
[INFO] Copying 6 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ MyProject ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 62 source files to C:\Users\mylogin\myproject\target\classes
... omitted ...
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ MyProject ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 6 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ MyProject ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 12 source files to C:\Users\mylogin\myproject\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.18.1:test (default-test) @ MyProject ---
[INFO] Surefire report directory: C:\Users\mylogin\myproject\target\surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.mycompany.myproject.test.behavior.steps.SampleRestCallWithInvalidAccountSteps
Processing system properties {}
Using controls EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=true,ignoreFailureInView=false,verboseFailures=true,verboseFiltering=false,storyTimeouts=300,threads=1,failOnStoryTimeout=false]
Running story com/mycompany/myproject/test/behavior/steps/sample_rest_call_with_invalid_account_steps.story
Generating reports view to 'C:\Users\mylogin\myproject\target\jbehave' using formats '[stats, ide_console, txt, html]' and view properties '{navigator=ftl/jbehave-navigator.ftl, views=ftl/jbehave-views.ftl, reports=ftl/jbehave-reports.ftl, nonDecorated=ftl/jbehave-report-non-decorated.ftl, decorated=ftl/jbehave-report-decorated.ftl, maps=ftl/jbehave-maps.ftl}'
Reports view generated with 2 stories (of which 1 pending) containing 1 scenarios (of which 1 pending)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.326 sec - in com.mycompany.myproject.test.behavior.steps.SampleRestCallWithInvalidAccountSteps

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-war-plugin:3.0.0:war (default-war) @ MyProject ---
[INFO] Packaging webapp
[INFO] Assembling webapp [MyProject] in [C:\Users\mylogin\myproject\target\mywarname##0.1]
[INFO] Processing war project
[INFO] Copying webapp resources [C:\Users\mylogin\myproject\src\main\webapp]
[INFO] Webapp assembled in [484 msecs]
[INFO] Building war: C:\Users\mylogin\myproject\target\mywarname##0.1.war
[INFO]
[INFO] --- spring-boot-maven-plugin:1.3.5.RELEASE:repackage (default) @ MyProject ---
[INFO]
[INFO] --- jbehave-maven-plugin:4.0.4:run-stories-as-embeddables (run-stories-as-embeddables) @ MyProject ---
[INFO] Running stories as embeddables using embedder Embedder[storyMapper=StoryMapper,embedderMonitor=MavenEmbedderMonitor,classLoader=EmbedderClassLoader[urls=[/C:/Users/mylogin/Projects/AQ/aqservices/target/test-classes/, /C:/Users/mylogin/Projects/AQ/aqservices/target/classes/, spring-boot-starter-tomcat-1.3.5.RELEASE.jar, tomcat-embed-core-8.0.33.jar, tomcat-embed-el-8.0.33.jar, tomcat-embed-logging-juli-8.0.33.jar, tomcat-embed-websocket-8.0.33.jar, spring-ws-core-2.3.0.RELEASE.jar, spring-xml-2.3.0.RELEASE.jar, spring-aop-4.2.6.RELEASE.jar, spring-beans-4.2.6.RELEASE.jar, spring-core-4.2.6.RELEASE.jar, spring-oxm-4.2.6.RELEASE.jar, spring-web-4.2.6.RELEASE.jar, spring-webmvc-4.2.6.RELEASE.jar, spring-boot-starter-1.3.5.RELEASE.jar, spring-boot-1.3.5.RELEASE.jar, spring-boot-autoconfigure-1.3.5.RELEASE.jar, snakeyaml-1.16.jar, spring-boot-starter-log4j-1.3.7.RELEASE.jar, jcl-over-slf4j-1.7.21.jar, jul-to-slf4j-1.7.21.jar, slf4j-log4j12-1.7.21.jar, log4j-1.2.17.jar, spring-orm-4.3.2.RELEASE.jar, spring-jdbc-4.2.6.RELEASE.jar, spring-tx-4.2.6.RELEASE.jar, spring-data-commons-1.12.2.RELEASE.jar, spring-context-4.2.6.RELEASE.jar, spring-expression-4.2.6.RELEASE.jar, spring-context-support-4.1.4.RELEASE.jar, spring-test-4.2.6.RELEASE.jar, spring-security-core-4.1.1.RELEASE.jar, aopalliance-1.0.jar, spring-security-web-4.1.1.RELEASE.jar, spring-security-config-4.1.1.RELEASE.jar, jstl-1.2.jar, wsdl4j-1.6.3.jar, aspectjweaver-1.8.9.jar, jasypt-1.9.2.jar, xstream-1.4.9.jar, xmlpull-1.1.3.1.jar, xpp3_min-1.1.4c.jar, bcprov-jdk16-1.46.jar, org.apache.commons.io-2.4.jar, commons-io-2.4.jar, slf4j-api-1.7.21.jar, commons-lang-2.6.jar, mail-1.4.jar, activation-1.1.jar, dozer-5.5.1.jar, commons-beanutils-1.9.2.jar, commons-lang3-3.2.1.jar, jregex-1.2_01.jar, tomcat-embed-jasper-8.0.33.jar, ecj-4.5.jar, ehcache-2.10.2.2.21.jar, HikariCP-2.4.7.jar, emdq-1.0.jar, sqljdbc4-4.0.jar, ghs-verifyaq-client-1.0.3.jar, mockito-all-1.9.5.jar, junit-4.11.jar, hamcrest-core-1.3.jar, jbehave-core-4.0.4.jar, hamcrest-library-1.3.jar, hamcrest-integration-1.3.jar, commons-collections-3.2.2.jar, plexus-utils-3.0.10.jar, freemarker-2.3.23.jar, paranamer-2.4.jar, jbehave-spring-4.0.4.jar],parent=ClassRealm[plugin>org.jbehave:jbehave-maven-plugin:4.0.4, parent: sun.misc.Launcher$AppClassLoader@55f96302]],embedderControls=UnmodifiableEmbedderControls[EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=false,ignoreFailureInView=false,verboseFailures=false,verboseFiltering=false,storyTimeouts=300,threads=1,failOnStoryTimeout=false]],embedderFailureStrategy=<null>,configuration=org.jbehave.core.configuration.MostUsefulConfiguration@645b2ac7,candidateSteps=<null>,stepsFactory=<null>,metaFilters=<null>,metaMatchers=<null>,systemProperties={java.awt.headless=true},executorService=<null>,executorServiceCreated=false,performableTree=PerformableTree,storyManager=<null>,timeoutParsers=<null>]
[INFO] Found class names: [com.mycompany.myproject.test.behavior.steps.SampleRestCallWithInvalidAccountSteps]
[INFO] Using controls UnmodifiableEmbedderControls[EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=false,ignoreFailureInView=false,verboseFailures=false,verboseFiltering=false,storyTimeouts=300,threads=1,failOnStoryTimeout=false]]
[INFO] Running embeddable com.mycompany.myproject.test.behavior.steps.SampleRestCallWithInvalidAccountSteps
[INFO] Processing system properties {java.awt.headless=true}
[INFO] System property 'java.awt.headless' set to 'true'
[INFO] Using controls UnmodifiableEmbedderControls[EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=false,ignoreFailureInView=false,verboseFailures=false,verboseFiltering=false,storyTimeouts=300,threads=1,failOnStoryTimeout=false]]
[INFO] Generating reports view to 'C:\Users\mylogin\myproject\target\jbehave' using formats '[stats, ide_console, txt, html]' and view properties '{navigator=ftl/jbehave-navigator.ftl, views=ftl/jbehave-views.ftl, reports=ftl/jbehave-reports.ftl, nonDecorated=ftl/jbehave-report-non-decorated.ftl, decorated=ftl/jbehave-report-decorated.ftl, maps=ftl/jbehave-maps.ftl}'
[INFO] Reports view generated with 2 stories (of which 1 pending) containing 1 scenarios (of which 1 pending)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.714 s
[INFO] Finished at: 2017-03-28T13:25:28-04:00
[INFO] Final Memory: 38M/299M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.jbehave:jbehave-maven-plugin:4.0.4:run-stories-as-embeddables (run-stories-as-embeddables) on project MyProject: Failed to run stories as embeddables: Failure in running embeddable: com.mycompany.myproject.test.behavior.steps.SampleRestCallWithInvalidAccountSteps: NullPointerException -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.jbehave:jbehave-maven-plugin:4.0.4:run-stories-as-embeddables (run-stories-as-embeddables) on project MyProject: Failed to run stories as embeddables
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:212)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
        at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
        at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
        at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
        at org.apache.maven.cli.MavenCli.execute(MavenCli.java:863)
        at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
        at org.apache.maven.cli.MavenCli.main(MavenCli.java:199)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
        at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
        at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.MojoFailureException: Failed to run stories as embeddables
        at org.jbehave.mojo.RunStoriesAsEmbeddables.execute(RunStoriesAsEmbeddables.java:20)
        at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207)
        ... 20 more
Caused by: org.jbehave.core.embedder.Embedder$RunningEmbeddablesFailed: Failure in running embeddable: com.mycompany.myproject.test.behavior.steps.SampleRestCallWithInvalidAccountSteps
        at org.jbehave.core.embedder.Embedder.runAsEmbeddables(Embedder.java:140)
        at org.jbehave.mojo.RunStoriesAsEmbeddables.execute(RunStoriesAsEmbeddables.java:18)
        ... 22 more
Caused by: java.lang.NullPointerException
        at org.jbehave.core.steps.spring.SpringStepsFactory.stepsTypes(SpringStepsFactory.java:39)
        at org.jbehave.core.steps.AbstractStepsFactory.createCandidateSteps(AbstractStepsFactory.java:34)
        at org.jbehave.core.embedder.PerformableTree$RunContext.<init>(PerformableTree.java:458)
        at org.jbehave.core.embedder.PerformableTree.newRunContext(PerformableTree.java:1105)
        at org.jbehave.core.embedder.StoryManager.runStories(StoryManager.java:100)
        at org.jbehave.core.embedder.StoryManager.runStoriesAsPaths(StoryManager.java:86)
        at org.jbehave.core.embedder.Embedder.runStoriesAsPaths(Embedder.java:213)
        at org.jbehave.core.junit.JUnitStory.run(JUnitStory.java:24)
        at org.jbehave.core.embedder.Embedder.runAsEmbeddables(Embedder.java:131)
        ... 23 more
[ERROR]
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

My story file, named sample_rest_call_with_invalid_account_steps.story:

Scenario: Call the sample rest resource
Given an initial value
When this API is called
Then expect failure

My java story class, named SampleRestCallWithInvalidAccountSteps.java:

import com.mycompany.myproject.test.behavior.driver.AbstractSpringJBehaveStory;
import com.mycompany.myproject.test.behavior.driver.annotations.BehaviorTest;

import org.jbehave.core.annotations.*;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


/**
 * Scenario: Call the sample rest resource
 */
@RunWith(SpringJUnit4ClassRunner.class)
@BehaviorTest
public class SampleRestCallWithInvalidAccountSteps extends AbstractSpringJBehaveStory {

    @Given("an initial value")
    public void Given() {
        // purposely empty
    }

    @When("this API is called")
    public void When() {
        // purposely empty
    }

    @Then("expect failure")
    public void Then() {
        // purposely empty
    }
}

My story superclass:

package com.mycompany.myproject.test.behavior.driver;

import java.util.Arrays;

import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.MostUsefulConfiguration;
import org.jbehave.core.embedder.Embedder;
import org.jbehave.core.embedder.EmbedderControls;
import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.LoadFromClasspath;
import org.jbehave.core.io.StoryLoader;
import org.jbehave.core.io.StoryPathResolver;
import org.jbehave.core.io.UnderscoredCamelCaseResolver;
import org.jbehave.core.junit.JUnitStory;
import org.jbehave.core.reporters.FilePrintStreamFactory;
import org.jbehave.core.reporters.Format;
import org.jbehave.core.reporters.StoryReporterBuilder;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.ParameterControls;
import org.jbehave.core.steps.spring.SpringStepsFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;


public abstract class AbstractSpringJBehaveStory extends JUnitStory {

    @Autowired
    private ApplicationContext appContext;

    public AbstractSpringJBehaveStory() {
        Embedder embedder = new Embedder();
        embedder.useEmbedderControls(embedderControls());
        embedder.useMetaFilters(Arrays.asList("-skip"));
        useEmbedder(embedder);
    }

    @Override
    public InjectableStepsFactory stepsFactory() {
        return new SpringStepsFactory(configuration(), appContext);
    }

    @Override
    public Configuration configuration() {
        return new MostUsefulConfiguration()
                .useStoryPathResolver(storyPathResolver())
                .useStoryLoader(storyLoader())
                .useStoryReporterBuilder(storyReporterBuilder())
                .useParameterControls(parameterControls());
    }

    private EmbedderControls embedderControls() {
        return new EmbedderControls()
                .doIgnoreFailureInStories(true)
                .doGenerateViewAfterStories(true)
                .doVerboseFailures(true);
    }

    private StoryPathResolver storyPathResolver() {
        return new UnderscoredCamelCaseResolver();
    }

    private StoryLoader storyLoader() {
        return new LoadFromClasspath();
    }

    private StoryReporterBuilder storyReporterBuilder() {
        return new StoryReporterBuilder()
                .withCodeLocation(CodeLocations.codeLocationFromClass(this.getClass()))
                .withPathResolver(new FilePrintStreamFactory.ResolveToPackagedName())
                .withFailureTrace(true)
                .withDefaultFormats()
                .withFormats(Format.IDE_CONSOLE, Format.TXT, Format.HTML);
    }

    private ParameterControls parameterControls() {
        return new ParameterControls()
                .useDelimiterNamedParameters(true);
    }
}

For reference, I was loosely following this guide.

1

There are 1 best solutions below

0
On

I now have a working solution. I believe that I have narrowed the culprit to improperly extending AbstractSpringJBehaveStory in the SampleRestCallSteps class. I fixed this by creating a new empty class to serve as an instantiable subclass of AbstractSpringJBehaveStory and removing the extends from SampleRestCallSteps.

Also, I did find a few missing lines when creating the Configuration, which I believe is the real cause. I got the new ones that are now shown in the method below from generating a blank new JBehave-Spring archetype with maven.

@Override
public Configuration configuration() {
    ParameterConverters parameterConverters = new ParameterConverters();
    ExamplesTableFactory examplesTableFactory = new ExamplesTableFactory(
            new LocalizedKeywords(),
            new LoadFromClasspath(this.getClass()),
            parameterConverters);
    parameterConverters.addConverters(
            new DateConverter(new SimpleDateFormat("yyyy-MM-dd")),
            new ExamplesTableConverter(examplesTableFactory));
    return new MostUsefulConfiguration()
            .useStoryLoader(storyLoader())
            .useStoryParser(new RegexStoryParser(examplesTableFactory))
            .useStoryReporterBuilder(storyReporterBuilder())
            .useStoryPathResolver(storyPathResolver())
            .useParameterControls(parameterControls())
            .useParameterConverters(parameterConverters);
}

Lastly, I think a Spring context was not being autowired properly, so I changed the stepsFactory() method like this (again copied from the maven template):

@Override
public InjectableStepsFactory stepsFactory() {
    ApplicationContext appContext = new SpringApplicationContextFactory("jbehave-app-context.xml")
            .createApplicationContext();
    return new SpringStepsFactory(configuration(), appContext);
}