In developing a rails API, BDD way with cucumber, I opted for several compromises.
I tried to hide as much of incidental details as possible, in order to keep the definitions readable to the stakeholders, but still retained some imperativeness, so that it can serve as documentation for the client developers.
To help me with JSON management, I am using the json_spec gem. It has a number of useful matchers (such as be_json_eql, include_json, have_json_size…), helpers (parse_json, normalize_json…) and some common cucumber steps for checking the responses.
Installation and configuration is straightforward, it needs to be added to the Gemfile:
and to make use of json_spec cucumber steps they need to be required, and json_spec also needs to know the last_json. Rack::Test is part of rails, so its last_response is what I use.
Writing the definitions
The pragmatic programmers’ The cucumber book in a chapter about testing the REST web services, proposes that the definitions should be written like this :
At first I tried doing it like that, but soon my feature files became quite hard to read, especially if a scenario needed to include several tables and a JSON. So I decided to get rid of as much of incidental details as possible, but I kept the imperative definitions that call specific endpoints with appropriate methods, because they serve as a good reference to the client developers and they don’t bother the stakeholders much.
After applying these principles, the previous example would look something like this:
Instead of seeding the data from the definitions, I rather just specify what I want in the definition and do the seeding in the steps. I check if the response is what was expected, I add some additional fields (success, status, href) and check for those, I return the fruits in an array, and check the array length and contents:
Of course, transformations, helper methods and constants should go into their own respective files.
Checking the http headers
Here are several handy steps for checking the ACCEPT, CONTENT_TYPE and AUTHENTICATION headers:
Rather then calling that authentication step from the definitions, its much nicer to nest it in a definition like this:
When I authenticate as admin
and this would be the step:
- github: collectiveidea / json_spec