None of the work that we did earlier on our service is robust at this stage.
Yes, we have an automated test that proves that it talks to other objects around it. But those objects aren’t real – they’re mocks, designed to look like other objects. This is because the specification we were writing was a design document, not a description of the function.
So instead we need to write a “feature”; that is a document that describes how a particular function works. And then we back this up with real code that tests that it does what it says it does. And this time we will not be using mock objects, we will start up a small web-server and drive the application as if we were a real user, clicking real links and buttons, and making sure that the system behaves as expected.
We will be using a tool called “Spinach”. This is a descendant of an earlier tool called “Cucumber”, both of which use a language called “Gherkin” (hungry yet?). A lot of people don’t like this format – partly because the Gherkin language is quite verbose and programmers like to think in code, not English. Plus cucumber has a couple of issues where it’s easy, once your application starts to grow, to get into a mess.
Spinach cures the cucumber issues – it isolates each individual feature into its own file, so no matter how large your application is, each individual function stands alone.
And as for gherkin – I think it is extremely useful to have a written description, in English, of what you are trying to achieve before you start building it. If you write the equivalent feature in, for example, RSpec, you are diving right into the code, which can often distract you with implementation details. Instead, gherkin makes you write out what you want in human format, without any thought to the code that will complete it. An added advantage is that you can also show the feature to your client and they will understand what you are about to build.
So what does a feature file look like? (Note that this feature is not related to the service we were describing earlier, it just happens to be one that I am working on at the moment).
1 Feature: User updates product codes via an import
3 As a user of the product warehouse
4 I would like to update the codes for several of my products in one go
5 So that they reflect our current coding practices
8 Given I am logged in as a user for a client
9 And I have permission to update product codes via an import
10 And there are several products attached to this client
12 Scenario: accessing the importer
14 When I visit the home page
15 Then I should see the menu item
17 Scenario: successfully updating the codes
19 When I visit the product code importer page
20 And I upload my CSV of new product codes
21 Then I should see a message informing me that my product codes will be updated shortly
22 When some time has passed
23 Then the product codes should be updated to match the codes in the CSV
25 Scenario: uploading an invalid CSV
27 When I visit the product code importer page
28 And I upload an invalid CSV of product codes
29 Then I should see a message informing me that the CSV did not match the required format
31 Scenario: with insufficient permission
33 Given I no longer have permission to update product codes
34 When I visit the home page
35 Then I should not see the menu item
36 When I visit the product code importer page
37 Then I should see a message saying that I do not have permission to continue
There are a number of elements here.
Firstly, the title – “User updates product codes via an import”. The key thing here is that we are specifying who is doing the work – this becomes important in applications where different types of user have different sets of permissions. In this case, we are talking about “users” versus “administrators”.
Secondly, we have the abstract, the description. This is here to tell us why we are doing this – what is the value of this story? There are a couple of different formats you can use here – I like the “as a [particular class of person] I would like to [perform an action] so that [some result occurs]“.
Next, we have the “background” stage. This is any set up, any factors that are common to all the scenarios listed below – basically a convenience factor to prevent repetition and to make the scenarios more readable.
And then we come to the meat of it – the scenarios. Almost every feature will have at least two, possibly three scenarios. These are “the happy path”, the “unhappy path” and, sometimes, the “blank slate”.
The “happy path” is when everything goes as expected; in this case “successfully updating the codes”. My user goes to the application, does the bare minimum to make things happen and everything works perfectly.
The “unhappy path” is where something goes wrong. The user does not have permission, something is not set up correctly, the wrong data is entered and so on. In this example, we have two unhappy paths – “uploading an invalid CSV” and “insufficient permission”.
And the “blank slate” (which is not included in this particular example as it’s not really relevant) is a description of what the user sees when there is no data available for this function – for example, what happens when you log in to your application for the very first time and it is empty. All the best applications ensure that the user isn’t left hanging, but instead guide the user through getting started – which is why the “blank slate” path is useful.
These stories are extremely useful. Writing out the story titles as bullet points gives you a basis for discussing and examining the scope of an application. As the stories are expanded into full feature documents, you have the opportunity of discussing exactly what you are building with your client, in a format that they can understand. And as you are writing them, the stories provide the perfect unit for estimating how much work a particular feature is going to be – useful in both time estimates and pricing the feature to your client.
The next stage is going from an English (gherkin) story to some code that actually does the work …