This post has been migrated to http://www.thinkcode.se/blog/2014/05/29/cucumberjvm-hello-world
This is the example I showed at the I T.A.K.E. Unconference 2014 in Bucharest. I created it for your convenience so you should be able to implement it yourself after the presentation.
Before we dive into the example, let me just recap what I am aiming for. I will show you how an example (or specification if you want) can be executed. The example is written in plain text and it is used as the basis for an execution. This example can later be relied upon for regression testing as well as living documentation.
There are five files involved in the example. They should live this directory structure:
example |-- pom.xml `-- src |-- main | `-- java | `-- se | `-- thinkcode | `-- itake | `-- Belly.java `-- test |-- java | `-- se | `-- thinkcode | `-- itake | |-- RunCukesTest.java | `-- StepDefinitions.java `-- resources `-- se `-- thinkcode `-- itake `-- belly.feature
The feature file
Let us start by examining the feature file. This is the plain text file where the example is defined. It follows a specific syntax called Gherkin.
A feature file must start with the keyword Feature and it should be followed by a description of the feature. This description could be long and be written on many lines. It could be a user story. The description could be followed of a user story. It is all up to you how it should look like. The only important thing here is that the description is descriptive and easy to understand.
Next important thing is the Scenario. It should describe the steps that will be executed for this example. Each step begins with any of the keywords Given/When/Then and is followed by the actual step.
This is all that is needed to write a scenario in a feature. A feature may consist of many scenarios where each scenario describe a certain usage example of the system. There is only one scenario in this small example, but you can add many more.
The Cucumber runner
Some boilerplate code is needed to execute the example. I use a JUnit to execute Cucumber. It can be implemented as
This is a very small class that only defines that it should be executed by a JUnit runner that invokes Cucumber, The class name ends with Test. This means that the Surefire plugin in Maven will be able to pick it up and execute it. The feature file must be located in the same package as the runner. Cucumber will examine its classpath and search for all files with the suffix .feature and execute any scenarios it can find.
The steps needed to execute the example are not allowed to be implemented in this class. The steps needed must be implemented in other classes.
Glue the feature to Java code
The third file needed in this example is a class that implements the actual steps that should be executed. I implement them in a utility class called StepDefinitions. This is the glue that will connect the feature files found with the actual system. My implementation looks like this:
There are three methods defined. One for each step in the example above. Each method is annotated with a Gherkin keyword followed by a regular expression that should match the step in the feature. A regular expression group is used to find a parameter that should be picked up from the example. The first method is getting a digit that describes how many cukes that has been eaten. The steps must be located in the same package or a subpackage relative to the JUnit class that will execute Cucumber.
The methods needed to map steps to actual Java code may be implemented in many step definition files. It was convenient to use just one in this example, but if it had made sense I could have implemented the methods in many classes. All step methods are global and visible to Cucumber. This means that the functionality needed may be implemented in more than one class. Global functions may feel horrible but there is an cunning idea behind having them global. If you describe your system using the exact same words and mean two different areas of your system, you have a larger problem than global methods. You will need to address this so parts that does different things in your system actually is described differently.
I also need some production code that actually implements my belly. It is implemented in the class Belly.
The final thing needed to complete the example is a Maven pom that will help us get the dependencies we want and run the test. A Maven pom that satisfies this requirement is:
Finally run the example by executing
in the root directory where pom.xml is located.
All dependencies will be downloaded the first time you run Maven. This may take some time.
This is a very small example, nothing more than a Hello World. It should be small enough so you are able to implement it without getting lost among all the details. It is at the same time large enough to show how you can execute a plain text example. To prove to yourself that the example actually is used, change the number of cukes eaten to 17 and re-run. This should fail. Change back to 42, re-run and watch it pass.
- Cucumber hello world – the official hello world example I have adapted
- Cucumber – the official web site for Cucumber
- I T.A.K.E. Unconference 2014 – a conference where this example was presented
- Maven – the build system used
- The slides from the presentation
- My other Cucumber posts
- Thomas Sundberg – the author