Practical Cucumber: Organization
This post is part of our Practical Cucumber series.
Nobody knows how to organize cucumber features and steps. Unlike our beloved web framework, there are not enforced convention. Every project has it’s own conventions–that is, if it has conventions at all. Here is a pattern that works for us.
Group steps by model
Steps should be reusable. We’ve found the best way to keep track of them is to group them by the primary model they affect. Some steps may affect multiple models, but usually there is an obvious choice. For example, the following step could either go in provider_steps.rb
or license_steps.rb
.
Given '"$name" is licensed in "$state"' do |name, state|
provider = Provider.find_by_name!(name)
Factory(:license, :provider => provider, :state => state)
end
While it is related to the Provider
model, there are two good reasons it should go in license_steps.rb
- The step is creating a
License
, while it is only finding aProvider
Provider
is the most central model in this app. It will likely have a lot more related steps, so for findability’s sake, we will put it inlicense_steps.rb
Grouping steps by model makes them easy to find and encourages
Group features by…feature
Brilliant, huh? It seems obvious, but we often see apps where the features are grouped by model. There will be one massive appointment.feature
model that has completely unrelated scenarios. Put each feature in it’s own file. It’s up to you to decide what a “feature” is.
Here is one subdirectory of the project that I took the above example from:
$ ls features/scheduling_appointment/
canceling_appointment.feature
client_requesting_appointments_with_insurance.feature
client_requesting_chat_appointment.feature
client_requesting_phone_appointment.feature
client_requesting_video_appointment.feature
client_responding_to_rescheduled_appointment.feature
expiring_pending_appointments.feature
marking_an_appointment_complete.feature
provider_avoiding_scheduling_conflicts.feature
provider_requesting_appointment_with_an_unknown_user.feature
provider_requesting_phone_appointment.feature
provider_responds_to_appointment_request.feature
provider_scheduleing_an_appointment.feature
rescheduling_a_scheduled_appointment.feature
Which brings up another point: don’t be afraid to put features in subdirectories. For any large app, it’s almost essential. Cucumber gets confused when you do this, but you can fix that by adding the following to the options in cucumber.yml:
--require features/step_definitions --require features/support
Thoughts?
What do you do to organize your cucumber features and steps?
This post is part of our Practical Cucumber series.
Comments
The .yml file is part of the Cucumber gem. If we add these requires it will work fine locally. How might we easily distribute the change across multiple teams and environments as an automation package? Currently we use Bundler to package all of the gems required for our automation framework. A custom cucumber gem is probably not the best solution. Can we override the .yml file?
I’m kind a fan of the RESTful approach for smaller projects.. I group features by Model-CRUD description, as in feature files for: company_create, company_read, company_update, and company delete.
True, the files can get a little long, but I just keep the few @wip features I’m working on at the top, and run an overall rcov coverage test of everything at the end of the day just to keep myself honest.
Point is, I know right away where to put a feature by just thinking RESTfully.
Again, for small projects, the few custom steps I may have just go in an extra_steps.rb file.
Nice blog!
Carl Shaulis: we check the .yml file into git with our projects. You can certainly override it or add to it.
I am looking to do some stand alone testing against a Tapestry web app using cucumber. There are many roles in this application. How should I incorporate the roles in cucumber? Should I incorporate the roles in the directory structure or prefix the feature file names with the role.
Thanks, Charlie
I am looking to do some stand alone testing against a Tapestry web app using cucumber. There are many roles in this application. How should I incorporate the roles in cucumber? Should I incorporate the roles in the directory structure or prefix the feature file names with the role.
Thanks, Charlie
Charlie Cummins: We’ve had good luck with directories, but if you end up with a lot of directories with only one or two files, try to group by type of action and have each role’s version of that in the directory.