In the last segment, I managed to get JBehave reporting under Maven using a pre-canned example. This time, I want to tackle the other extreme – I want to develop a single story in JBehave and see what’s the bare minimum it takes to get it running, inside an IDE (in my case, Eclipse)/
There’s a bunch of different ways to run JBehave, but the easiest looks to be leveraging JUnit. They even provide a base class to inherit from – JUnitStory. The simplest possible JUnitStory class looks like this:
package net.twasink.jbehave.simple.stories; import org.jbehave.core.junit.JUnitStory; public class CookBaconStory extends JUnitStory { }
And that will work. Kind of. It will look for a file called 'cook_bacon_story.story'
in the same package on the classpath, and run it. Assuming a story like this one:
Narrative: In order to learn how to use JBehave better As a developer of some middling-level of competence I want to write some example stories about cooking bacon Scenario: Cooking bacon in a frypan Given 12 strips of bacon And a frypan on a medium heat When I put the bacon into the frypan And wait for 10 minutes or until the bacon sizzles And flip the bacon And wait for 5 minutes Then I get perfectly cooked crispy bacon
Then I get output like this:
Processing system properties {} Using controls EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=false,ignoreFailureInView=false,verboseFailures=false,verboseFiltering=false,storyTimeoutInSecs=300,threads=1] Running story net/twasink/jbehave/simple/stories/cook_bacon_story.story Generating reports view to '/Users/robertdw/personal/jbehave-spike/jbehave-simple-story/target/jbehave' using formats '[]' and view properties '{defaultFormats=stats, decorateNonHtml=true, viewDirectory=view, decorated=ftl/jbehave-report-decorated.ftl, reports=ftl/jbehave-reports-with-totals.ftl, maps=ftl/jbehave-maps.ftl, navigator=ftl/jbehave-navigator.ftl, views=ftl/jbehave-views.ftl, nonDecorated=ftl/jbehave-report-non-decorated.ftl}' Reports view generated with 0 stories (of which 0 pending) containing 0 scenarios (of which 0 pending)
Hmm. It has found the story, and run it – you even get an error if the story file doesn’t exist. But the bit at the end says “generated with 0 stories (of which 0 pending) containing 0 scenarios (of which 0 pending)”. Doesn’t sound good.
I dunno – you think that the basic infrastructure would report the stories that it finds. Anyway, I know that the full example works – so I take that and start paring it back until I get some output that makes sense. This is what I ended up with:
package net.twasink.jbehave.simple.stories; import org.jbehave.core.configuration.Configuration; import org.jbehave.core.configuration.MostUsefulConfiguration; import org.jbehave.core.junit.JUnitStory; public class CookBaconStory extends JUnitStory { @Override public Configuration configuration() { return new MostUsefulConfiguration(); } }
And now my output looks like this:
Processing system properties {} Using controls EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=false,ignoreFailureInView=false,verboseFailures=false,verboseFiltering=false,storyTimeoutInSecs=300,threads=1] Running story net/twasink/jbehave/simple/stories/cook_bacon_story.story Generating reports view to '/Users/robertdw/personal/jbehave-spike/jbehave-simple-story/target/jbehave' using formats '[]' and view properties '{defaultFormats=stats, decorateNonHtml=true, viewDirectory=view, decorated=ftl/jbehave-report-decorated.ftl, reports=ftl/jbehave-reports-with-totals.ftl, maps=ftl/jbehave-maps.ftl, navigator=ftl/jbehave-navigator.ftl, views=ftl/jbehave-views.ftl, nonDecorated=ftl/jbehave-report-non-decorated.ftl}' Reports view generated with 1 stories (of which 1 pending) containing 1 scenarios (of which 1 pending)
So now, using the ‘most useful configuration’, I’ve got a report view that actually condescends to admit that my scenario exists.
The next step is to add some, well, steps. With JBehave, when using the JUnitStory anyway, you do this by overriding the stepsFactory()
method. Now my code looks like this:
package net.twasink.jbehave.simple.stories; import net.twasink.jbehave.simple.steps.BaconCookingSteps; import org.jbehave.core.configuration.Configuration; import org.jbehave.core.configuration.MostUsefulConfiguration; import org.jbehave.core.junit.JUnitStory; import org.jbehave.core.steps.InjectableStepsFactory; import org.jbehave.core.steps.InstanceStepsFactory; public class CookBaconStory extends JUnitStory { @Override public Configuration configuration() { return new MostUsefulConfiguration(); } @Override public InjectableStepsFactory stepsFactory() { return new InstanceStepsFactory(configuration(), new BaconCookingSteps()); } }
My BaconCookingSteps
class isn’t complicated – writing steps isn’t the point of this spike. Here it is:
package net.twasink.jbehave.simple.steps; import org.jbehave.core.annotations.Given; import org.jbehave.core.annotations.Then; import org.jbehave.core.annotations.When; public class BaconCookingSteps { @Given("12 strips of bacon") public void howManyStripsOfBacon() { System.out.println(">>>> you're making how many pieces of bacon?"); } @Given("a frypan on a medium heat") public void heatTheFrypan() { System.out.println(">>>> how hot did you want that?"); } @When("I put the bacon into the frypan") public void startCooking() { System.out.println(">>>> Yeah - put that stuff there"); } @When("wait for 10 minutes or until the bacon sizzles") public void cookUntilSatisifed() { System.out.println(">>>> I'm hungry! Why are we waiting?"); } @When("flip the bacon") public void flipBacon() { System.out.println(">>>> These aren't panckes!"); } @When("wait for 5 minutes") public void cookSomeMore() { System.out.println(">>>> More waiting?"); } @Then("I get perfectly cooked crispy bacon") public void nomNomNom() { System.out.println(">>>> Finally! Nom nom nom nom"); } }
And when I run this, I get this output:
Processing system properties {} Using controls EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=false,ignoreFailureInView=false,verboseFailures=false,verboseFiltering=false,storyTimeoutInSecs=300,threads=1] Running story net/twasink/jbehave/simple/stories/cook_bacon_story.story >>>> you're making how many pieces of bacon? >>>> how hot did you want that? >>>> Yeah - put that stuff there >>>> I'm hungry! Why are we waiting? >>>> These aren't panckes! >>>> More waiting? >>>> Finally! Nom nom nom nom Generating reports view to '/Users/robertdw/personal/jbehave-spike/jbehave-simple-story/target/jbehave' using formats '[]' and view properties '{defaultFormats=stats, decorateNonHtml=true, viewDirectory=view, decorated=ftl/jbehave-report-decorated.ftl, reports=ftl/jbehave-reports-with-totals.ftl, maps=ftl/jbehave-maps.ftl, navigator=ftl/jbehave-navigator.ftl, views=ftl/jbehave-views.ftl, nonDecorated=ftl/jbehave-report-non-decorated.ftl}' Reports view generated with 1 stories (of which 1 pending) containing 1 scenarios (of which 1 pending)
So – that’s how you write a bare minimum JBehave story to run inside an IDE. This isn’t too complicated – after all, I can always add a new base class to hold that “most useful configuration” bit. JUnitStory itself isn’t much bigger, either – less than 10 lines of code, really. Similarly, if I really wanted to, I could combine the Story and the Steps together into one class, wire it all up in the parent, and reduce my overhead to making my POJO steps extend a parent class.
All of that sounds pretty good. So what’s the problem? Well, the problem is how it looks in the IDE.
That’s just crap – the test scenarios don’t show up as tests. The JUnitStory, as far as JUnit is concerned, is one test. That story file may have a thousand scenarios – it’s still one test. That’s not acceptable, IMHO – we’ll have to do something about that later.
But not now. For now, I’ve worked out what a minimal story looks like. Before I start solving new problems, I want to solve the next thing on my list: using dependency injection (in particular, Spring). That will be my next post.
concerning the look of the tests in eclipse, have a look at jbehave-junit-runner (http://blog.codecentric.de/en/2012/06/jbehave-configuration-tutorial/)
In the course of coaching and teaching BDD I’ve introduced a lot of people to this page and they all have had nothing but praise for your simple, yet informative description of the subject – the last person make the following comment: “I wish JBehave web site could outsource the documentation to Robert – he couldn’t do any worse than them!!!” Thank you on behalf of many people.
Hi, I have read all your JBehave posts and you make some very valid points. Was just interested if you found a way to display all scenarios in a junit test?
Very nice article for a beginner!!! Excellent Job!!
Hi there,
I’m trying your example but when I run the test I always get the following output:
org.jbehave.core.io.StoryResourceNotFound: Story path ‘com/jbehave/cook_bacon_story.story’ not found by class
As I see, everything is in place and I don’t know where to look. Could you give me a point?
Thanks
Marius, you haven’t put the file on the classpath correctly. Make sure you have a file called ‘cook_bacon_story.story’ in the same package as the test.
Hi Robert
Hope you are still looking after this blog.
I used your example but always got this exception:
java.lang.NoClassDefFoundError: com/thoughtworks/paranamer/Paranamer
at org.jbehave.core.ConfigurableEmbedder.(ConfigurableEmbedder.java:40)
at org.jbehave.core.junit.JUnitStory.(JUnitStory.java:16)
at net.twasink.jbehave.simple.stories.CookBaconStory.(CookBaconStory.java:12)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:195)
at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:244)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:241)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.ClassNotFoundException: com.thoughtworks.paranamer.Paranamer
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
… 25 more
William, I hope you’ve managed to solve your problem. `ClassdefNotFound` is usually a secondary exception, in response to an error while loading a class – so I’d look for an earlier error.
Hello, did you have any spare time to work on better integration of jBehave with Junit features?
Upfront thanks for your reponse,
Cheers!
Nope. Wish I did, but my family commitments chew up most of my spare time. Heck, I can’t even catch up with the handful of comments I get on my blog. 😉
Hi.
I have the same problem as william with your example.
The only error is that was posted by william (“java.lang.NoClassDefFoundError: com/thoughtworks/paranamer/Paranamer”)
This is the link to my Eclipse project: https://cloud.mail.ru/public/HLBRBQcGndQU/JBehaveTests.7z
I have solved this problem: you should add all the dependent libraries to you Eclipse project (it is about 80 *.jar files for jbehave 3.9.5). All of them can be downloaded from Maven Central Repository using http://jbehave.org/reference/stable/dependencies.html manual(one should know what is ANT and Maven). But, yes jbehave manual is very tricky for beginners.