This is a follow-up on my previous post. As usual, the link to the source code is provided at the end of this article. The example is inspired by the Apache Camel show case. So there we go - a simple application to report incidents in an imaginary factory.
The very first user story gives a good idea of what is going on:
Story: Report Incident
As a customer
I want to be able to report an incident so that it can be resolved as quickly as possible
so that I can mitigate unforeseen outages
Given a customer called ACME Factory is logged in
When an incident described as Engine stopped working is submitted
as a MAJOR problem
Then the new incident should be described as Engine stopped working
And the new incident should be classified as MAJOR
And the new incident should be bound to ACME Factory
And the submission time should be the current time
And the status should be SUBMITTED
And a new email should be sent to admin@crashtracker.com,
subject: ACME Factory: New Incident Report,
message: Major Problem: Engine stopped working
The story is formatted in sync with BDD requirements. It has a name, describes the purpose (the added value) and defines the workflow by using the Given - When - Then syntax. Most importantly, it provides input on problem severity, customer name and other relevant entries. It even defines the expectations towards the automated email notification. Other stories follow the same rules.
The whole process of capturing incidents and dealing with them is reflected by the contract the implementation should adhere to:
public interface CrashTracker {
Long captureIncident(String description, IncidentSeverity severity);
boolean assignEngineer(Long incidentId, String engineer);
void captureOutput(Long incidentId, IncidentOutput output);
}
The crash tracker's implementation is the subject to thorough testing. The stories are paired up with their Java counterparts. The essential wiring is done in a dedicated class managed by the JBehave framework. What follows is an excerpt showing the significant details:
@RunWith(SpringAnnotatedEmbedderRunner.class)
@Configure(
parameterConverters = ParameterConverters.EnumConverter.class)
@UsingEmbedder(
embedder = Embedder.class,
generateViewAfterStories = true,
ignoreFailureInStories = true,
ignoreFailureInView = true)
@UsingSpring(resources = {"classpath:app-config.xml"})
public class StoryTest extends InjectableEmbedder {
private static Wiser wiser;
public StoryTest() {
incidentManager =
new ClassPathXmlApplicationContext("app-config.xml")
.getBean(IncidentManager.class);
assertNotNull(incidentManager);
}
@Before
public void setUp() {
wiser = new Wiser();
wiser.setPort(2500);
wiser.start();
}
@After
public void tearDown() {
wiser.stop();
}
@Test
@Override
public void run() throws Throwable {
injectedEmbedder().runStoriesAsPaths(storyPaths());
}
protected List storyPaths() {
return new StoryFinder()
.findPaths(
codeLocationFromPath("src/main/resources"),
"**/*.story", "");
}
// Static utility methods shared by all steps
}
The class inherits from the JBehave's InjectableEmbedder. That creates a nice set-up for the Spring dependency injection.
Further, notice the EnumConverter configuration. By default, only primitives and their wrappers are accepted as arguments in the JBehave step methods. Complex data types and enums require explicit converters.
Next, pay attention to the overridden run() method. It takes the advantage of the fact that the whole class is treated as a JUnit test. This is the entry point to subsequent steps, i.e. the actual tests. Notice how the story wiring is done. The StoryFinder is again JBehave's utility enabling to look up relevant user stories.
Finally, the best bit - an embedded SMTP server, thanks to SubEtha SMTP. JUnit lifecycle annotations are used to start the server prior to any steps are entered and to stop it at the very end.
Once the wiring is in place, each story is bound to exactly one Java class called step. Here is a brief example:
@Steps
public class ReportIncidentSteps {
@Resource
private CrashTracker crashTracker;
..
@Given("a customer called $username is logged in")
public void aCustomer(String username) {
..
}
@When(
"an incident described as $description is submitted as a
$severity problem")
public void anIncidentIsSubmitted(String description,
IncidentSeverity severity) {
..
}
@Then("the new incident should be described as $description")
public void theDescriptionShouldBe(String description) {
..
}
// etc. according to the relevant story
}
The text in the Given - When - Then annotations has to be identical with the content of the relevant story so that it can be paired up. The $ prefix turns the appropriate chunk of the story line into the test method argument. Last thing to note is the @Steps annotation. This subtle but important feature enables the step class be automatically looked up by Spring. Credit goes to Alex.
This brings us to the end of the test configuration. Simple text files (user stories) have been turned into testable Java code. Thanks to JBehave features and support of DI it has been achieved with a very little effort.
Next time, I will wrap up the topic by providing details on process automation via the Activiti framework. To download the source code of the fully implemented example please use the link below.
Download Source Code or Explore It
Update: An on-request example of JBehave + TestNG integration. Enjoy!