Avoid using case statements in your cucumber/specflow/jbehave step definitions

I quite frequently come across a scenario that looks something like this:

Scenario: Create some animals
Given I am a zoo keeper
When I create a giraffe
And I create a lion
And I create a pony
And I create a unicorn
Then I should have a zoo

and step definitions that implement the When steps with a single step definition:

[sourcecode language=”ruby”]
When /^I create a (\D+)$/ do |animal|
case animal
when ‘lion’
when ‘giraffe’
when ‘pony’
raise ‘Unknown animal’

I don’t like having case statements in steps for a number of reasons:

  • For readability and maintainability reasons I try to keep my step definitions as short as possible (usually a couple of lines), and using a case statement violates this principle;
  • Raising an exception to catch invalid usage of the step (in an else clause) replicates what these BDD frameworks already do, provide feedback about unimplemented steps;
  • IDEs that support step auto completion (such as RubyMine & Visual Studio) will not suggest valid steps as they don’t understand how you’ve implemented a case statement; and
  • If used inappropriately (such as our unicorn step), the test will only fail at run-time whereas most IDEs will highlight non-matching steps as you’re writing.

For example, you could change our steps to look something like this:

[sourcecode language=”ruby”]
When /^I create a lion$/ do

When /^I create a giraffe$/ do

When /^I create a pony/ do

Even though this is three times as many step definitions, it is actually less code (9 lines compared to 12).

By using this approach it is obvious we can’t currently create a unicorn as RubyMine tells us before we even run our tests. And we don’t need to raise any exceptions myself.

rubymine highlights unimplemented steps

Whilst a lot of people use case statements in steps to reduce the number of steps, it is actually counter intuitive as it means you have more code to do so, and the outcome is less usable when writing scenarios. So, please avoid putting case statements in your step definitions.

Author: Alister Scott

Alister is a Software Quality Engineer for Automattic.

6 thoughts on “Avoid using case statements in your cucumber/specflow/jbehave step definitions”

  1. I agree with this principle and have found case statements making things more difficult in general.

    What about a pattern like this, though, that does the same thing in one step/2 lines?

    When /^I create an? (lion|pony|giraffe)$/ do |animal|

    1. Yeah, I like it.
      The only issue I have is I am using JBehave at the moment and it doesn’t allow regular expression captures, so I can’t do it!

    2. this still just pushes the case statement into create_an_animal and leaves you with a rather generic step; which can lead to spaghetti when you have a project with 100s of steps…

      Since I know you’re using ruby, you could take it a step further and push everything down to specific instance methods; and then DRY up the code should the creation of some animals be the same with a meta programmed method..

      This way you can actually document with yardoc what’s going on, what your permutations of animal choice might be, and your absolutely explicit as to what each step definition is doing.

      In your project:
      class ZooKeeper

      def create_a_pony; [something complicated and unique] end

      def create_a_animal(:type)

      def method_missing

      [logic that handles creating a generic animal]


      In your step_def:
      When(/^I create a lion$/), :create_a_lion, :on zoo_keeper
      When(/^I create a pony$/), :create_a_pony, :on zoo_keeper

      In your env.rb:
      module Driver
      def zoo_keeper
      @zoo_keeper ||= ZooKeeper.new

      some what borrowed from: https://github.com/cucumber/cucumber/blob/master/features/docs/defining_steps/one_line_step_definitions.feature

      As for Java though, this is a reason to use ruby ;)

      1. While I think the actual one line examples they showed at cuke up are cool, I’m not ready to embrace them, yet.

        Whether they get put in world, an included module, a controller class or a page object, I hope we can agree that we should minimize cluttering the steps with things like this.

        And cuke regex and Ruby meta programming go together almost too well. Meta Program All The Things! :-)

  2. Great stuff Alister. Read this when first posted, but came back to it today as I refactored some code (removing Case statments) with this in mind.

    I think some of the best points here come down to allowing you to make better use of the IDE. Knowing when a step does not exist. Being able to jump exactly to the step code, not to a step containing a giant switch and then having to find your ‘when’ case. Yes it’s more steps, but each is short and sweet and furthermore your IDE will help you to find them

    Regarding Titus’s comment, I’ve seen some pretty ‘cool’ tricks done with meta-programming that I like a lot (the page framework test-factory for example).. and on cases like that I think it’s great. Otoh I’ve also seen it create situations which start to be come entirely like ‘magic happens here’ and can make it far harder to follow and troubleshoot/debug code when something is not working. instead of knowing exactly what code was executed, you have to go back, figure out what values were captured, and what that ended up causing the code to do.

    Re-usable steps are very seductive, doing this with metaprogramming is doubly so. But excessive pursuite of this sort of thing can lead you down a bad path if you start letting them dictate the wording of steps and how scenarios are expressed. it’s the ‘one ring to rule them’ trap in a sense, the allure of writing one step to replace 30 different steps that takes you into a ‘ends justifies the means’ place that results in crufty scenarios

    In particular I think while a great demonstration of really cool elegant code, that writing highly generalized cucumber steps of this sort to reduce 20 different “given I am on the xx page” steps down to a single step starts to head over into the land of obfuscated and difficult to troubleshoot/debug code. It also starts to IMHO result in a case where the language of the steps in scenarios is determined not by what expresses business values while reading well and making sense to stakeholders, but instead wording that fits into the pattern of your metaprogrammed uberstep.. that is as some say ‘topping from the bottom’ which is to say, at least IMHO that step phrasing should favor the ‘specification’ half of ‘executable specification’

    The other thing that happens is that it becomes easy for the scenarios to slide towards incorporating implementation details in order to allow highly reusable steps which look like this.

    When /^They click (.*) under (.*) page$/ do |locator,page_name|
    @page_object = Kernel.const_get(to_class(page_name)).new

    That’s really cute, and notice that it could replace an almost countless number of steps. But also notice that the step wording now includes details like clicking on specific controls etc, instead of staying focused on the business value. So you get steps like this that start to specify the names and labels of controls, pages, etc. And not just the names of those things in the UI, but how they are identified inside your page_object framework. So you get details from an abstraction layer, at least two levels below the feature (feature -> step -> page_object) pushing themselves up into the wording of the scenario. Now the scenario is no longer expressing ‘what’ the user wants to accomplish, but dwelling on ‘how you do it’ details that should be inside the step definition, not creeping up into the wording of the step itself.

Comments are closed.