1

I am using JBehave for writing BDD Integration tests.
Problem: JBehave clears state of objects (instance variables) while executing individual steps
Code: StepDefinition:

public class StepDefs {

    private String str;

    @Given("step represents a precondition to an $event")
    public void given(String event){
        str=event;
        System.out.println("Given: "+str);
    }

    @When("step represents the occurrence of the event")
    public void when() {
        System.out.println("When: "+str);
    }

    @Then("step represents the outcome of the event")
    public void then() {

    }
}

Story:

Sample story

Narrative:
In order to communicate effectively to the business some functionality
As a development team
I want to use Behaviour-Driven Development

Scenario:  A scenario is a collection of executable steps of different type
Given step represents a precondition to an event
When step represents the occurrence of the event
Then step represents the outcome of the event

JBehaveJUnitTestRunner:

@RunWith(JUnitReportingRunner.class)
public class JBehaveTestsRunner extends JUnitStories {

    private CrossReference xref = new CrossReference();

    public JBehaveTestsRunner() {
        configuredEmbedder().embedderControls().doGenerateViewAfterStories(true).doIgnoreFailureInStories(true)
                .doIgnoreFailureInView(true).doVerboseFailures(true);// .useThreads(1);
    }

    @Override
    public Configuration configuration() {
        Properties viewResources = new Properties();
        viewResources.put("decorateNonHtml", "true");
        return new MostUsefulConfiguration().useStoryLoader(new LoadFromClasspath(this.getClass().getClassLoader()))
                .useStoryReporterBuilder(
                        new StoryReporterBuilder().withFormats(Format.HTML, Format.CONSOLE, Format.STATS)
                                .withViewResources(viewResources).withFailureTrace(true).withFailureTraceCompression(false)
                                .withCrossReference(xref));
    }

    @Override
    public InjectableStepsFactory stepsFactory() {
        return new ScanningStepsFactory(configuration(), "stepdefs");
    }

    @Override
    public List<String> storyPaths() {
        StoryFinder finder = new StoryFinder();
        return finder.findPaths(CodeLocations.codeLocationFromClass(getClass()), Arrays.asList("**/Simple.story"), null);
    }
}

Actual Output:

Processing system properties {}
Using controls EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=true,ignoreFailureInView=true,verboseFailures=true,verboseFiltering=false,storyTimeouts=300,threads=1,failOnStoryTimeout=false]

(BeforeStories)

Running story stories/Simple.story
Sample story
(stories/Simple.story)
Narrative:
In order to communicate effectively to the business some functionality
As a development team
I want to use Behaviour-Driven Development
Scenario: A scenario is a collection of executable steps of different type
**Given: event**
Given step represents a precondition to an event
**When: null**
When step represents the occurrence of the event
Then step represents the outcome of the event



(AfterStories)

Generating reports view to 'C:\WORKING\lunaworkspace\pkeautomation\target\jbehave' using formats '[html, console, stats, junitscenarioreporter]' and view properties '{decorateNonHtml=true}'
log4j:WARN No appenders could be found for logger (freemarker.cache).
log4j:WARN Please initialize the log4j system properly.
Reports view generated with 3 stories (of which 1 pending) containing 2 scenarios (of which 1 pending)

As can be seen in the output: In the Given step I am accepting a string argument which i am initializing it to instance variable "str", whilst printing the value to console I can see it successfully. But when the second step i.e When step executes I am getting null as the value of instance variable "str". How can I make JBehave to not clear state of objects after executing individual steps?

Mrunal Gosar
  • 4,595
  • 13
  • 48
  • 71
  • Possible duplicate of [JBehave maintain data across steps Given/When/Then during a Scenario](http://stackoverflow.com/questions/9389845/jbehave-maintain-data-across-steps-given-when-then-during-a-scenario) - though I've just looked at that and it doesn't actually explain how to do it so retracting the close vote. Linking to answers without putting the actual content of those answers down is bad, people! – Lunivore Mar 16 '16 at 09:24

2 Answers2

2

For anyone coming looking for an answer for this question I did found proper resolution to this by taking help from the JBehave google group community. The solution is quite simple instead of using ScanningStepFactory Use InstanceStepsFactory and that should persist the state of the objects. Link to the discussion: Google Group discussion

Snippet for anyone coming here for answer:

package runner;

import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.MostUsefulConfiguration;
import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.LoadFromClasspath;
import org.jbehave.core.io.StoryFinder;
import org.jbehave.core.junit.JUnitStories;
import org.jbehave.core.reporters.CrossReference;
import org.jbehave.core.reporters.Format;
import org.jbehave.core.reporters.StoryReporterBuilder;
//import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.InstanceStepsFactory;
//import org.jbehave.core.steps.ScanningStepsFactory;
import org.junit.runner.RunWith;

import de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner;
import stepdefs.StepDefs;
//import stepdefs.BarStep;
//import stepdefs.FooStep;

@RunWith(JUnitReportingRunner.class)
public class JBehaveTestsRunner extends JUnitStories {

    private CrossReference xref = new CrossReference();

    public JBehaveTestsRunner() {
        configuredEmbedder().embedderControls().doGenerateViewAfterStories(true).doIgnoreFailureInStories(true)
                .doIgnoreFailureInView(true).doVerboseFailures(true);// .useThreads(1);
    }

    @Override
    public Configuration configuration() {
        Properties viewResources = new Properties();
        viewResources.put("decorateNonHtml", "true");
        return new MostUsefulConfiguration().useStoryLoader(new LoadFromClasspath(this.getClass().getClassLoader()))
                .useStoryReporterBuilder(new StoryReporterBuilder()
                        .withFormats(Format.HTML, Format.CONSOLE, Format.STATS).withViewResources(viewResources)
                        .withFailureTrace(true).withFailureTraceCompression(false).withCrossReference(xref));
    }

    /*@Override
    public List<CandidateSteps> candidateSteps() {
        return new InstanceStepsFactory(configuration(), new FooStep(), new BarStep(), new StepDefs())
                .createCandidateSteps();
    }*/

    @Override
    public InjectableStepsFactory stepsFactory() {
        return new InstanceStepsFactory(configuration(), new StepDefs());
        // return new ScanningStepsFactory(configuration(), "stepdefinitions");
    }

    @Override
    public List<String> storyPaths() {
        StoryFinder finder = new StoryFinder();
        return finder.findPaths(CodeLocations.codeLocationFromClass(getClass()), Arrays.asList("**/Sample.story"),
                null);
    }
}
Mrunal Gosar
  • 4,595
  • 13
  • 48
  • 71
1

It's been a while since I used JBehave. It may be that you're creating a new ScanningStepsFactory for each stepsFactory() call. Try creating just one of those in the constructor then pass that instance back so that you're not creating a new one for each call.

And if that fails, try using an InstanceStepsFactory as in the example here:

public abstract class NoughtsAndCrossesStory extends JUnitStory {

    public NoughtsAndCrossesStory() {
        Configuration configuration = new MostUsefulConfiguration()
            .useStoryPathResolver(new UnderscoredCamelCaseResolver(""))
            .useStoryReporterBuilder(new StoryReporterBuilder()
                .withCodeLocation(CodeLocations.codeLocationFromClass(this.getClass()))
                .withDefaultFormats()
                .withFormats(CONSOLE, TXT)
                .withFailureTrace(true));
        useConfiguration(configuration);
        WindowControl windowControl = new WindowControl();
        addSteps(new InstanceStepsFactory(configuration,new GridSteps(windowControl), new BeforeAndAfterSteps(windowControl)).createCandidateSteps());
     }

}

You'll need to create some kind of persistent repository object in which to hold your string (in the example above, windowControl persists).

public class BeforeAndAfterSteps extends Steps {

    private final WindowControl windowControl;

    public BeforeAndAfterSteps(WindowControl windowControl) {
        this.windowControl = windowControl;
    }

    @BeforeScenario
    public void beforeScenarios() throws Exception {
        windowControl.reset();
    }

    @AfterScenario
    public void afterScenarios() throws Exception {
        windowControl.destroy();
    }
}

This allows you not only to persist state across steps, but between scenarios. Please note that that's generally considered a bad practice; I'm using this here to ensure that state is not persisted between scenarios, but in theory you could use it to, for instance, initialize default data before running your test suite.

Lunivore
  • 17,277
  • 4
  • 47
  • 92
  • created singleton isntance of ScanningStepsFactory but didn't work. The other approach would be too much coding for such a simple requirement (as there will be lots of instance variables which i would want to maintain). Maybe JBehave is not the right tool for me to go ahead but for now I'll accept your answer if the second approach is what JBehave users would need to do. Thanks – Mrunal Gosar Mar 16 '16 at 13:06
  • If you've got a number of instance variables, consider creating a "world" class and storing all your instances on there. It would give you complete control, allow you to reset it, etc. - you can create a new one in a @BeforeScenario step and ensure that it's always fresh. Also consider asking the JBehave user list as my knowledge of JBehave is *old* (as is the example): http://jbehave.org/mailing-lists.html – Lunivore Mar 16 '16 at 20:04
  • Thanks for helping. I did ask in mailing list but it seems whatever you've said above is the only way of doing it. I've used cucumber JVM for BDD implementation, but wanted to try if JBehave is a better option or not..it seems i would need to stick to Cucumber for now. The solution which you've provided is too much of unnecessary coding as it is readily available in cucumber without doing anything. Again, appreciate your help here. Thanks a lot :) – Mrunal Gosar Mar 17 '16 at 05:20
  • The full sample code of "NoughtsAndCrosses" can be found at: https://github.com/jbehave/jbehave-core/tree/master/examples/noughtsandcrosses/src/test/java/com/lunivore/noughtsandcrosses – Peter A Mar 16 '18 at 10:23
  • @PeterA Hah, I actually wrote that... but a very long time before all the instance / factory code was written. Good to know people are still finding it useful! – Lunivore Mar 16 '18 at 12:25