Top 5 Cucumber Best Practices


Reading Time: 3 minutes


This is the sixth episode of our Testing Tuesday series. Every week we will share our insights and opinions on the software testing space. Drop by every Tuesday to learn more! Last week we talked about testing web applications with Selenium. This week we focus on cucumber best practices.

Sign up for a free Codeship Account

How to use Cucumber? – Cucumber best practices

Once you get started with Cucumber, the question is how to write your features. How can you keep your features maintainable so you don’t have to correct them after each change in the application? How can you reuse steps most efficiently? What are typical Cucumber smells?

Following I list 5 best practices that help us in our daily Cucumber life at Codeship.

1. Write declarative features

Scenarios should be written like a user would describe them. Beware of scenarios that only describe clicking links and filling in form fields, or of steps that contain code or CSS selectors. This is just another variant of programming, but certainly not a feature description.

Declarative features are vivid, concise and contain highly maintainable steps.

2. Insert a narrative

Narratives describe in about one sentence what a feature does. Typical narratives contain a benefit for the user, a role that needs the feature and the feature itself. Narratives are important to envision why you are implementing a feature in the first place. They also give a short overview of the feature so others get a rough understanding what it is about without reading the scenarios.

3. Avoid conjunctive steps

When you encounter a Cucumber step that contains two actions conjuncted with an “and”, you should probably break it into two steps. Sticking to one action per step makes your steps more modular and increases reusability. This is not a general rule though. There may be reasons for conjunctive steps. However, most of the time it’s best to avoid them.

4. Reuse step definitions

In Cucumber you can reuse steps in other steps. This comes in handy when a step extends another step’s behavior or defines a superior behavior that consists of multiple steps. You should try to reuse steps as often as possible. This will improve the maintainability of your app: If you need to change a certain behavior, you just need to change a single step definition.

5. Use backgrounds wisely

If you use the same steps at the beginning of all scenarios of a feature, put them into the feature’s Background. Background steps are run before each scenario. But take care that you don’t put too many steps in there as your scenarios may become hard to understand.

Up next week: Managing Test Data with factory girl

In next week’s Testing Tuesday #7 we’ll talk about test data. What’s the best way to create test data? How can you keep test data maintainable when your application data model changes? If you’ve got some tips on test data management or you can recommend a good test data tool, please drop me a line!

PS: If you liked this article you might also be interested in one of our free eBooks from our Codeship Resources Library. Download it here: Why Continuous Integration Is Important

Further information:

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles.
Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.

We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.

  • jemmyw

    4. Reuse step definitions

    I’m not so sure about this one. I used to do this a lot, but it becomes unmaintainable. A better idea, in my opinion, is to extract shared code into methods and call those methods from your steps. That way, not only is it more readable in the step file, you can also pass arguments instead of doing string interpolation on the steps you’re calling.

    • somebody32

      Agree, here is another talk from the latest CukeUp! Conf ( starting at 08:01) where Matt Wynne talking about not nesting steps and consider it as a bad practice.

      • Clemens Helm

        Thanks guys, especially for the link! In our codebase nesting steps is still handy, so I considered it a best practice. But I can see where this could be going in the long run.

        I will definitely try the method approach, especially with the shortcuts mentioned in the podcast, this is really awesome.

        Have you got some other recommendations as well? I’m already collecting best practices for another episode :)

      • 100% agree with jemmyw and somebody32 here. I’ve unfortunately come across some terrible examples of nesting step definitions and it’s a real pain to debug and maintain.

        Some good old fashion Ruby modules / methods can really help to share common code among step definitions and is really readable to developers as it’s just plain Ruby code.

        Clemens my personal best practice however, though only recently, is to try and extract any CSS / XPATH from my cucumber steps into their own modules. This way you can create a single module for any view objects such as a sign up form and keep all your markup specific code in there. This way any time you need to make any changes to the HTML to a specific widget on your site you have a single point of reference in your test library.

        I can send you some examples if you’d like to see some code.

    • Chuck_vdL

      I agree strongly with Jemmyw’s point. step re-use is good, but I’ve seen it taken far too far. Also you are usually better served to use patterns like Page objects and Data objects. Once you are doing that, the sign that you are trying to go too far in step re-use is if you have steps that are not in any scenarios, and only called by other steps, and if those steps consist of one or two lines, which are something like creating a page object and using one method from the page object or calling a single method from a data object like ‘@mynewuser.register

      In that case you’d be better off just to have that stuff directly in your primary step, not in a step that is called by the primary step.

    • gtramontina

      I share the same opinion as you. Have you experimented with Spinach ( It works as cucumber but without the regex crazyness.

    • walketim

      I’m of mixed mind on this. I’ve done it a lot both ways, (step reuse) and steps calling common helper methods. I’ve had this debate many times and with a lot of people. The thing that keeps me thinking that direct step reuse is a good idea is that, in my mental model of BDD, steps become “sentence fragments” in the grammer and dialect of the language being spoken (the DSL). This seems to be extraordinarily powerful and natural to me. As living documentation this just seems like the better way to view the problem. Not sure to what extent this takes us but, frankly, I like the ‘idea of this’ a lot, enough to lead me to direct reuse while guarding against carelessness in any approach taken. Every word matters.

  • stewe

    leaving you a comment and making you happy! nice tutorial :)

    • Clemens Helm

      Thanks a lot, stewe, you made my birthday even sweeter :)

  • Andrew Vit

    Something like “When I sign up as” is easily understood as the process of filling in the form and submitting it. I like this level of abstraction. But the last example, “Then I should be able to …” is pretty vague and not at all clear what you’re actually testing. “Being able to” sounds like it wraps the whole feature into a single step: this would be better broken up into more concrete “I perform X” and “It should be done”.

    • Clemens Helm

      Right, but sometimes I just want to describe that an action could be done without really performing it, by checking if a UI element is present. Do you have an alternate suggestion for this or do you generally consider it a bad practice?

      • Chuck_vdL

        If there is complex logic around when an element or control is displayed or active then I could see value in cucumber examples to specify and test that behavior

        Otherwise what value do you deliver to the user by merely having the element present? The value comes in what using the element to accomplish some test. So write the cukes that specify and test that stuff, and once you have them the tests for presence of the element (sans exception noted above) are largely redundant.

  • Andrew Vit makes a good point. I disagree with opting for a much higher level of abstraction than the actual tasks that the user must perform to achieve something, because then when something fails it is often difficult for a developer to know where something broke. In addition to this, having to describe the process that the user actually goes through to accomplish something quickly demonstrates areas where your UX design needs work.

    For some time we wrote cucumber tests for a REST API by describing things in more human-readable language (e.g. “I create a user named ‘george'” instead of “I post the following payload to ‘/users'”). But over time our tests became very difficult to work with.

    I have seen similar problems in cucumber-based UI tests that describe actions too broadly (e.g. “When I register as a new user” instead of the actual process required to register. It may make sense in some cases to have steps like this, but if you’re not careful you end up doing a lot of work in your steps.

    I get suspicious whenever I see Cucumber steps that become a significant layer of abstraction between the developer and what the user actually does. Tests should be written from the user’s perspective–not some imaginary land where there is much hand-waving between what is being described and the work that actually has to take place to get it done.

    • Clemens Helm

      Hi Carl,

      I don’t agree with your statement for a number of reasons:

      > when something fails it is often difficult for a developer to know where something broke.

      Why is that? When something breaks, Cucumber tells you, which command (for example `click_link “Login”`) failed.

      > In addition to this, having to describe the process that the user actually goes through to accomplish something quickly demonstrates areas where your UX design needs work.

      You also describe this process in the step definitions. We usually also don’t use Cucumber to evaluate user experience, but user feedback.

      These problems you talk about: Can you name a few? I can imagine that too much abstraction also leads to problems, but you didn’t name any.

      > Tests should be written from the user’s perspective–not some imaginary land where there is much hand-waving between what is being described and the work that actually has to take place to get it done.

      So your users describe features by mentioning link texts, input labels (or ids) and css selectors?

      I’m just wondering because I made the opposite experiences so I’d be really thankful if you could list a few problems that you ran into!


      • >> when something fails it is often difficult for a developer to know where something broke.
        >Why is that?

        For example, let’s say you’re testing the process of creating a user, and you have a step that looks like, “And I create a user named Carl” and that this step fails. If this step is doing a lot of web clicking behind the scenes or if it does a lot of intermediate things to save you space in your cucumber scenario, it’s not always obvious which part of the process broke and it may take some time to pinpoint where the failure is occurring, whereas if you describe it in terms of “And I fill in ‘name’ with ‘Joe’ and I click ‘Create'” you can know almost immediately where the breakage is happening.

        Also, let’s suppose that the process of creating a user is burdensome and involves far more clicks than necessary. This will be more obvious to you as a developer if you have to spell out all the fields that must be completed and all the clicks that must be made. If you don’t have to type all these out, you will be far less likely to notice that the process could be streamlined. In this way, writing cucumber steps from the perspective of the user brings an added benefit.

        >> Tests should be written from the user’s perspective–not some imaginary land where there is much hand-waving between what is being described and the work that actually has to take place to get it done.
        >So your users describe features by mentioning link texts, input labels (or ids) and css selectors?

        It depends a lot on how well you’ve written your markup, but generally, yes, I think users tend to describe the process by saying things like “And I fill in ‘Name’ with ‘Carl’ and I click ‘Create'”. If you’ve written your markup well, there should be very little need to refer to arcane css selectors or ids in your cucumber tests. If you have to, it’s a good sign that your semantic markup needs work.

        • Hardik Shah

          I have few queries of similar nature Can you shed some light

          In the below example no 1. I have declared a variable which is passed as an argument to the underlying method, also i have declared words like check box & text field in the cucumber step definition.
          Which is better of the two examples for the same step & why

          1. “When I check the “Rate_Checkbox” then the “Rate_Text_field” is enabled”

          2. “When I enable Rate

          • Hardik, I think you should enhance your steps so that you can use the text inside the label of the checkbox instead of the DOM ID or input name. That would allow you to say something like:

            When I check “Monitor Rate”
            Then “Rate” should be enabled

            I definitely think your first approach is better than the second.

  • This is a great post about software testing. The information herein are truly helpful. Thanks for sharing.

  • bossyong

    Thank you master :)

  • Mike Potopowicz

    Hello everyone! i am new to this and I dont understand it can anyone help?

  • Pingback: Boas práticas de Cucumber e Calabash - Parte 2 | Blog da Concrete()

  • Pingback: Boas práticas de Cucumber e Calabash – Parte 02 -()

  • Pingback: ​3 Essential Basics for Setting up an Automation Suite for Web Apps | taisedias()

  • Pingback: Improve the architecture of your cross-platform automation | rafazzevedo()

  • Name

    Point 1 and point 3 seem contradictory to me.
    If I write declarative / high level steps then they will not be reusable
    I am not sure how to write a concise given-when-then format of an end-to-end test involving multiple web applications.
    I perform some operations in application 1 [[[note: these operations require tons of inputs, clicks and lot of data points]]]
    [[[I login to application 2]]] and I approve operations in application 1
    Something happens in application 3 [[[ many assertions based on data and input in application 1 and application 2]]]

    in above end to end scenario, there need to be 100s of assertions in each step.. Application 1, 3 and 3 represent different URLs. The test typically takes 5-7 minutes to run hence there are 100s of steps (clicks, inputs, etc) for these.

    Its really difficult to follow points 3 and 4 in such test cases.

    Is there a better framework (or method in BDD) to automate such test cases and still keep framework maintainable, scalable etc?

  • Pingback: Sobre Especificação Por Exemplo como ela é - Parte 2 - Teste de Software()

  • Pingback: Especificação Por Exemplo como ela é | Blog da Concrete()

  • Pingback: Especificação Por Exemplo como ela é – Parte 2 :: Concrete Solutions()

  • Pingback: Automatizando Testes de Interface Web em Ruby :: Concrete Solutions()

  • Pingback: Buenas prácticas de Cucumber - Federico Toledo()