Search code examples
cucumberspecflowgherkin

Gherkin scenarios, reusable steps approach or specific approach


I need advice on how to write scenarios. First I have to explain that we have a CQRS architecture where commands and queries are separated APIs. We specify the commands with Gherkin scenarios that are used in Specflow to create tests.

In the scenarios below the domain is that of Expenses. An expense bundle is a collection of expenses. With this scenario I want to specify and test that I can't create an expense for an expense bundle that someone else created. I can only create expenses for an expenses bundle that I created.

The following approach is that I try to reuse as many steps as possible:

Background: 
  Given I am declarant 'Marieke'

     Scenario: Not allowed to create expense for a bundle that was created by another declarant
     Given the following expense bundles exist
        | declarant | name             | administration    | status        |
        | Lucy      | Trip to New York | Company B.V.      | not submitted |
       When I create an expense for the following expense bundle
        | declarant | name             | administration    | status        |
        | Lucy      | Trip to New York | Company B.V.      | not submitted |
       Then the expense is not created for the expense bundle

The name, administration and status is maybe not relevant in above example. But in other scenario's I can reuse the 'given the following expense bundles exist' step. This saves time for the developer.

In the following approach I try to write a scenario that is better readable and more specific:

Background: 
  Given I am declarant 'Marieke'

    Scenario: Not allowed to create expense for a bundle that was created by another declarant
       When I create an expense for an expense bundle that was created by another declarant
       Then the expense is not created for the expense bundle

In this case the developer has to write a When step that will probably never be used again. Is this a problem?

I'm struggling with both options a lot in my scenarios. Any advice?


Solution

  • The way to write scenarios is too explain the WHAT and WHY and have nothing about HOW things are done. The WHAT is about claiming expense bundles and in particular that you can't claim expenses for someone else. You haven't really explained WHY that is important, you could do that in the features preamble. The scenario shouldn't give a damn about HOW expense bundles are. Other problems with the scenarios are that the language seems a bit clumsy. Again you could use the preamble to explain what an expense bundle is. So I would write something like

    Feature: Claiming an expense on an expense bundle
    
    Explain what an expense bundle is, including the concept of ownership
    Explain what an expense is
    Explain why Fred should not be able to claim an expense on Susan's expense bundle
    
    Background:
      Given users Fred and Susan
      And Susan has an expense bundle
    
      Scenario: Susan claims an expense
        When Susan claims an expense on her bundle
        Then the expense should be approved
    
      Scenario: Fred claims an expense on Susan's bundle
       When Fred claims an expense on Susan's expense bunlde
       Then the expense should be rejected
    

    This would be my starting point, and I would be using this to prompt questions like

    1. Why can Fred see Susan's expense bundle
    2. Should we notify someone (Susan, Freds boss, Fred) when Fred tries to claim the expense
    3. ...

    When cuking step definition reuse is irrelevant IF you write your step definitions correctly. The correct way to write a step definitions is to make each one a single call to a helper method. In this way step definitions are reduced to performing one single function - to translate business language into a call. So it doesn't matter if a step is only used once (most aren't) because writing it is trivial.

    Re-using code in helper methods is an entirely different problem, but now that we are fully in code (rather than being half way there in a step def) we can use all our normal code tools and skills to solve that problem.