I have started using Cucumber to write BDD tests to match the business use cases for my application. It is blockchain based so each user in the test is running an instance of the application. This also means that each test is quite heavy and 95% of the test time is in the setup stage.
As I write the tests I am finding that I am starting to repeat myself and the earlier features are seemingly becoming redundant.
One business flow is:
- User1 saves a new message
- User2 edits the message
- User1 verifies the edit
- User1 cancels the message
- User2 acknowledges cancellation
This gets broken down into New/Edit/Cancel features.
Initially I started out with "New" and "Edit" feature files looking like this:
New
Feature: a new message is added
Scenario: a user adds a new message
Given there is a user called User1
And there is a user called User2
When User1 creates a new message with id 1234
Then User2 should see the message with id 1234
Edit
Feature: Editing a message
Scenario: A User edits a message
Given there is a user called User1
And there is a user called User2
When User1 creates a new message with id 1234
And User2 adds the location US to the message
Then User1 should see the location US on the message
But now I am coming on to the Cancel part I realise that in order to test the Cancel properly, the system needs to have an edited message which means I need to have gone through the New and Edit features to get the message into the right state.
This would make the Cancel look something like this, which then is starting to get quite lengthy:
Cancel
Feature: Cancelling a message
Scenario: A User cancels a message
Given there is a user called User1
And there is a user called User2
When User1 creates a new message with id 1234
And User2 adds the location US to the message
And User1 cancels the message
Then User2 should see status Cancelled on the message
I could write the Cancel like this instead:
Feature: Cancelling a message
Scenario: A User cancels a message
Given there is a message with id 1234
And User1 cancels the message
Then User2 should see status Cancelled on the message
which reads quite well as a feature, however, I would now have to write a step definition for "there is a message with id 1234" that does everything that the Edit feature was doing.
As mentioned at the start, the setup in these tests takes 95% of the test time so ideally I would like to run these as a series of steps together rather than starting from fresh for each feature. e.g.
- Do the setup once
- Create a New message
- Edit the message
- Cancel a message
Is it possible to chain the scenarios or features together and reuse the system state from the previous one?
Or do I have to start the system from scratch each time?
Is a step definition that calls all the other steps/methods that make up the Edit feature the right way to go for the Cancel or should I write lots of And statements?
I can understand your frustrations with this, but there is no way to have subsequent features build on each other. Each scenario is meant to be atomic and repeatable. The problem with scenarios depending on each other is a failed scenario will cause cascading failures in the scenarios that follow. One failure in the application triggers multiple failed tests, causing your team to start thinking the tests are flakey.
There is nothing wrong with writing a step that simulates the previous scenarios — this is the right way to do it. When defining those steps, keep them as atomic as possible so they are very composable.
To be honest, a 6 step scenario is perfectly fine. The only change I would suggest is making Given
versions of your steps. The cancel scenario looks like it has to many When
's.
Feature: Cancelling a message
Scenario: A User cancels a message
Given there is a user called User1
And there is a user called User2
And User1 created a new message with id 1234
And User2 added the location US to the message
When User1 cancels the message
Then User2 should see status Cancelled