I attempted to use JBehave almost a year ago, but found it difficult to work with. In the last two months, however, JBehave 2.0 and JBehave 2.1 were released. These releases represent a fundamental change in how JBehave works, and are supposed to make using JBehave easier. I thought I’d try JBehave again. And, man oh man, it is so much better! Here are my notes.
Getting Started
In order to use the latest JBehave you need to be using Java 1.5 or greater. If you want to use JBehave with your IDE, you’ll need to make sure you are using JUnit 4. You can download JBehave here.
Creating the Tests
We’ll create a text file for the scenario using the words Given, When, Then and And.
Given the basic data set exists
When I do the basic work action
Then the basic result occurs
We need to save the scenario in file. The file should have a meaningful name, but use lowercase letters and underscores, eg: basic_work_scenario
Let’s develop a simple heuristic: basic_work_scenario describes some basic work scenario. Something happens. Causing other things to happen. One step leads to another.
In our code, then, we need a class that reflects the name of the text file:
package com.bitmotif.jbehaveexamples;
import org.jbehave.scenario.Scenario;
public class BasicWorkScenario extends Scenario {
}
With that in place, we should be able to run the scenario. In your IDE, select the BasicWorkScenario class and run it.
If you see something like
org.jbehave.scenario.errors.ScenarioNotFoundException:
Scenario com/bitmotif/jbehaveexamples/basic_work_scenario could not be found by classloader sun.misc.Launcher$AppClassLoader@a9c85c
That means the text file with the Given/When/Then could not be found. The easiest way to do this is to make sure the file is placed along with the compiled classes. In Intellij, I just set the compilation option to take anything: *?.
Now, when we run, we see something like this
Scenario:
Given the basic data set exists (PENDING)
When I do the basic work action (PENDING)
Then the basic result occurs (PENDING)
We now have an outline of what we expect to see, as well as what has been implemented in the scenario. We haven’t implemented anything, so it is still pending. Also, note that you got a green bar.
Let’s continue developing our heuristic. Scenarios have steps, right? So, in our case the BasicWorkScenario has steps BasicWorkScenarioSteps. We need to create BasicWorkScenarioSteps. And, it turns out in JBehave, steps are a type. So, we also have:
package com.bitmotif.jbehaveexamples;
import org.jbehave.scenario.Scenario;
public class BasicWorkScenarioSteps extends Steps {
}
We can integrate our BasicWorkScenarioSteps with our BasicWorkScenario like this:
package com.bitmotif.jbehaveexamples;
import org.jbehave.scenario.Scenario;
public class BasicWorkScenario extends Scenario {
public BasicWorkScenario() {
super( new BasicWorkScenarioSteps() );
}
}
If we run this, we still get a green.
We still don’t have anything interesting. Our BasicWorkScenarioSteps needs to contain the steps needed for the scenario. This translates to:
package com.bitmotif.jbehaveexamples;
import org.jbehave.scenario.steps.Steps;
import org.jbehave.scenario.annotations.Given;
import org.jbehave.scenario.annotations.When;
import org.jbehave.scenario.annotations.Then;
public class BasicWorkScenarioSteps extends Steps {
@Given("the basic data set exists")
public void createBasicDataSet() {
}
@When("I do the basic work action")
public void basicWorkAction() {
}
@Then("the basic result occurs")
public void checkResult() {
}
}
Note the annotations and how they tie to our original text. If we run the scenario, we still get a passing run. Let’s see something interesting. What happens if we put in assertions that fail?
package com.bitmotif.jbehaveexamples;
import org.jbehave.scenario.steps.Steps;
import org.jbehave.scenario.annotations.Given;
import org.jbehave.scenario.annotations.When;
import org.jbehave.scenario.annotations.Then;
import org.junit.Assert;
public class BasicWorkScenarioSteps extends Steps {
@Given("the basic data set exists")
public void createBasicDataSet() {
Assert.assertTrue(false);
}
@When("I do the basic work action")
public void basicWorkAction() {
Assert.assertTrue(false);
}
@Then("the basic result occurs")
public void checkResult() {
Assert.assertTrue(false);
}
}
When we run this, we get the following:
Scenario: Given the basic data set exists (FAILED) When I do the basic work action (NOT PERFORMED) Then the basic result occurs (NOT PERFORMED) java.lang.AssertionError: at org.junit.Assert.fail(Assert.java:91) at org.junit.Assert.assertTrue(Assert.java:43) at org.junit.Assert.assertTrue(Assert.java:54)
That’s a little more interesting! Note that after the first failure, the subsequent steps are not run.
Now, let’s hook up our scenario to a “real” system. For our purposes, we have two pieces we need to hook in: the system under test, and our data source. We’ll create these:
package com.bitmotif.jbehaveexamples;
public class StubbedDataSource {
private boolean basicDataSetCreated = false;
public boolean createBasicDataSet() {
basicDataSetCreated = true;
return true;
}
public boolean isInBasicState() {
return basicDataSetCreated;
}
}
And
package com.bitmotif.jbehaveexamples;
public class StubbedSystem {
private boolean actionWasSuccessful = false;
public boolean performBasicWorkAction() {
actionWasSuccessful = true;
return true;
}
public boolean isInBasicState() {
return actionWasSuccessful;
}
}
We’ll need to modify BasicWorkScenarioSteps like this:
package com.bitmotif.jbehaveexamples;
import org.jbehave.scenario.steps.Steps;
import org.jbehave.scenario.annotations.Given;
import org.jbehave.scenario.annotations.When;
import org.jbehave.scenario.annotations.Then;
import org.junit.Assert;
public class BasicWorkScenarioSteps extends Steps {
private StubbedSystem system = new StubbedSystem();
private StubbedDataSource dataSource = new StubbedDataSource();
@Given("the basic data set exists")
public void createBasicDataSet() {
Assert.assertTrue(dataSource.createBasicDataSet());
}
@When("I do the basic work action")
public void basicWorkAction() {
Assert.assertTrue(system.performBasicWorkAction());
}
@Then("the basic result occurs")
public void checkResult() {
Assert.assertTrue(system.isInBasicState());
Assert.assertTrue(dataSource.isInBasicState());
}
}
And when we run it, we get green. We’ve essentially done the old “fake until you make it” routine. If we wanted to, we could now move toward swapping in a real data source (perhaps a database?) and real system to test.
Conclusion
The new JBehave is much easier to work with than its predecessor. When it comes to BDD, Java is now a first class citizen.
I used this example while using Eclipse 3.4. I got all the results you said I would but at the end of the sample it did not work.
When I run it as is I get an error saying:
“Source not found
The source attachment does not contain the source for the file JUnitScenario.class. You can change the source attachment by clicking Change Attachment Source below”
I have done that and re-downloaded the libraries and reattached them. That didn’t solve the problem. If I change either the text file (last line to have one character that is different)
For example right now the file “basic_work_scenario” has the last line reading “Then the basic result occurs”
If I change this to “Then the Basic result occurs” i still get this error but I will have an output stating
Scenario:
Given the basic data set exists
When I do the basic work action
Then the Basic result occurs (PENDING)
It should have to be a lower case ‘b’ in basic since that’s what the BasicWorkScenarioSteps.java file is looking for.
Any ideas??
@Laura,
I suspect this is an IDE issue.
If you found a solution, would you mind posting it?
[...] Jbehave2 Basics http://jbehave.org/documentation/two-minute-tutorial/ http://www.bitmotif.com/java/jbehave-rave/ [...]
[...] Jbehave2 Basics http://jbehave.org/documentation/two-minute-tutorial/ http://www.bitmotif.com/java/jbehave-rave/ [...]