The feature I will start with looks like this:
It consists of three parts:
- Given – the preconditions of the system under test. The setup of the systems state if you want. In this case make 18 compact cars available for rental in the system.
- When – the actual change of the system. Transforming it from the initial state to the final state. Rent one car.
- Then – the expected final state of the system. The verification that the state change was the desired change. After one car is rented, there should only be 17 left to rent.
Next step is to connect the feature above with the System Under Test, SUT. A couple of things are needed.
- Parse the feature file
- Locate proper code to execute the different steps
- Execute steps that uses the system under test
The feature file is just plain text so parsing it is easy. Cucumber uses a parser called Gherkin to do this.
Cucumber then searches the class path to find any methods annotated with regular expressions that will match each
Given/When/Then part of the feature. There must only be one method, step, which matches the regular expression in the classpath. These methods are global. The reasoning behind making them global is that they describe a part of the system. If two different parts are described the same way, then they are the same and the same step definition can be used in both cases. If you have described two different parts of the system with the exact same wording, then you have an issue with ambiguity. Take some time and understand why you describe different parts of a system the same way. Chances are that they actually are the same thing and that you have some refactoring to do.
When the proper method has been located, it will be executed.
Cucumber can currently be executed using two different methods.
- A command line tool
- A JUnit runner
I choose to execute it using the JUnit runner. Connecting through JUnit will make it a seamless part of a project developed using tests. I don’t have to supply the proper classpath that includes all features, all steps and the SUT. All I have to do is setup a Maven project where I locate the files according to the Maven standard and it just works.
The JUnit glue code is implemented as:
Naming the class
*Test will make Mavens Surefire plugin able to pick it up and execute it. Notice that this is an empty class. This is by design. Cucumber demands that you implement the code that actually does something, the steps, in separate classes in either the same package or a sub package.
The feature files must be located in the same package or a sub package, as the JUnit runner class is located.
The final thing needed to be able to execute the feature is to implement the steps that actually setup the system, execute it and finally assert its new state. The first iteration will be done like this:
This is a skeleton of the steps we want to execute on the system we are building. Before I move on, lets take a look at what we have.
Three methods. All three of them are annotated with
@Then. Each annotation has a string parameter. The string parameter is a regular expression that will be used when matching the feature file with proper methods. Each group in the regular expression, indicated with a pair of parenthesis, will be extracted as a parameter to the method. The group
(\d) would match a digit,
(\d+) any digits. Unfortunately we are dealing with Java here so ‘\’ has to be escaped and the group for matching any number of digits need to be expressed as
The step definitions above will not perform anything valuable. They are a starting point towards steps that actually do something that has a value. If you execute them, they will behave as an ignored unit test. Some text reminding you to implement them will be printed.
A model implementation
Now is a good time to implement the initial domain model needed for behaviour above. I will drive the initial implementation from the steps. As it looks now I will need
- Some kind of entity object representing a Car
- A place to store Cars
- A method to ask for the numbers of cars available given a specific category
- A method to select a car from specific category and define it as rented
Hopefully this will be enough for this first iteration of the domain model.
Before I start implementing the model, I want to implement the steps that will verify the model. If I did it the other way around and started with the model and implemented the test steps afterwards it would not be driven by my need for a specific behaviour. Therefore I start with an implementation of the steps like this:
We recognise the three methods from earlier. The difference now is that they actually do something.
I am not really happy with the first method, it has a loop and I really don’t like repetition in a test method. Repetitions add unnecessary complexity. I will take care of that a bit later.
The other thing we notice is that I have an instance of a data access object. It is a reference to the model that I use to store cars in and later retrieve cars from. I have to keep a state somewhere and an in-memory database is sufficient for the moment.
The remaining two methods should be more or less self-explaining. I reduce the number of available cars by renting one. I ask the system how many cars that are available and expect one less compared with when the system was setup.
The model I implemented to satisfy the steps above looks like this. I start with the data access object, CarDAO:
It needs a Car class to work with and it is implemented as:
This may be the simplest possible solution that could work. It is probably more or less useless in a production system. We might be interested in more things than if the car is rented or not. We might be interested in when a rental period started and when it ended, not just that the car is rented. But once again, this is a starting point and I want to keep the number of details as low as possible.
Finally I have implemented an in memory version of the CarDAO interface. It stores the cars in a list and iterates through the list for almost every operation. Not very efficient, but it is enough to start with. The implementation looks like this:
Currently I have three layers. I have the feature, the specification or example, layer. Below that there is a layer of glue code that will connect the feature with the system under test. At the bottom I have a model. This is the production code that I actually might ship. This may or may not be enough, depending on your setup.
Different people are good at different things. There are programmers that really suck on testing and there are testers that really suck on programming. If we assume that the definition of the problem, the feature, belongs to the testers area of expertise and that the model belongs to the programmers area of expertise. How do we divide the problem so we don’t force people that are good at writing code but bad at testing to do what they actually do best?
One way forward may be to simplify the api that the step definitions uses as much as possible. This would make the writing of the steps significantly easier. I need to introduce some help code that actually connects to the system under test to be able to do that. By doing that, however, I have the chance to divide the solution in two distinct parts that either belongs to the tester domain or that belong to the programmers domain. This division opens up for separating who actually implements each part and that is not something I actually think is good. I always think that it is a lot better if a tester and a programmer cooperate in finding a good solution.
Backed by this reasoning, I will introduce some help classes that will move the complexity from the steps and into a helper class instead. This will allow me to remove the repetition in the step above. It will also allow me to remove the reference to a domain object in the step definition.
My first step is to create a help class like this:
Naming utility classes like this is difficult. If you have any better suggestion for a name, please let me know.
The help class is obviously very similar to the old step definitions. It still contains a repetition. But now it belongs to the programmer’s domain and repetition is ok here.
With the complexity moved, the new step definitions will look like this:
The steps now consist of one and two line methods and the complexity is really low. This should be possible to read for both a tester and a programmer. A tester should be able to maintain and create new steps. They might want some initial assistance, but they should be able to add their own steps quite soon.
If more actions are needed on the system under test, they should be added in the proper help class. I would expect that a library of help classes eventually would emerge for a larger system than this small example.
The last thing needed to actually implement this example is a project file where the dependencies are defined. I use Maven and the Maven pom is defined as:
The file organisation follows the normal Maven structure. I have a total of eight files in this example and they are located like this:
model |-- pom.xml `-- src |-- main | `-- java | `-- se | `-- waymark | `-- rentit | `-- model | |-- dao | | |-- CarDAO.java | | `-- InMemoryCarDAO.java | `-- entiy | `-- Car.java `-- test |-- java | `-- se | `-- waymark | `-- rentit | |-- RunCukesTest.java | `-- steps | |-- RentACarSupport.java | `-- RentStepdefs.java `-- resources `-- se `-- waymark `-- rentit `-- Rent.feature
All production code is located in ‘main’. All test code, features, steps, help classes etc. are located in ‘test’
This is all that is needed to use BDD and Cucumber to build a model that might the simplest possible solution. Next step is to use the same behaviour and add some user interfaces to it. I will build a web application in JSF.