Friday, January 29, 2016

How To Write More Maintainable BDD Tests

Given we are writing BDD style requirements
And we are using cucumber syntax
And we want to be able to respond to changes
When we write our requirements
Then we should write them in a way that requires as few changes to them when requirements change
And we should minimize the number of tests that are impacted by changes in requirements.


We can write such tests in the following way:

Feature: Say Hello
User Story: As a marketer, I want the system to greet users by name when they log in so that the system feels more user friendly.

Given a user with the name "bob"
When bob logs in
Then the system should say "Hello, Bob!"

Since this is a simple case, it is sufficient to write the test in this way.

For a more extensive use case that is dependent on bits and pieces of data from many sources with complex business rules, it would be better to define the test cases in the following way:

Feature: Greet User
User Story: As a marketer, I want the system to provide customized greetings to users based on their purchasing habits so that the users will be more likely to increase their overall purchases.


Now things are getting interesting because we need to have a bunch of business rules that deterministically choose the proper greeting.

First we can set up some fake data as Background.

Background:
useritemdate
bobcoffee2020-12-21
bobshoes2020-12-21
bobcoffee2021-01-15
bobcoffee2021-02-15
marycoffee2021-01-15
pattea2021-01-15
pattea2021-02-15
patcar2021-02-15
pattea2021-03-15

Scenario: User regularly purchases an item
Given today is 2021-03-17
When each user logs in
Then the system will respond with the following:

usergreeting
bobHello, bob! Would you like me to add coffee to your cart?
maryHello, mary!
patHello, pat!

Scenario: User has recently made a large, unique purchase
Given today is 2021-02-17
When each user logs in
Then the system will respond with the following:

usergreeting
bobHello, bob! Would you like me to add coffee to your cart?
maryHello, mary!
patHello, pat! I hope you are enjoying the car you recently purchased! Is there anything you need related to the car?


As you can see I am repeating myself a bit, so we have room to refactor.

Given the following dates:
2021-03-17
2021-03-17

When each user logs in
Then the system will respond with the following for the reasons stated:

dateusergreetingreason-meta
2021-02-17bobHello, bob! Would you like me to add coffee to your cart?bob purchased coffee mid-month each month and has not purchased coffee yet this month
2021-02-17maryHello, mary!default greeting
2021-02-17patHello, pat!default greeting
2021-03-17bobHello, bob! Would you like me to add coffee to your cart?User regularly purchases an item
2021-03-17maryHello, mary!default greeting
2021-03-17patHello, pat! I hope you are enjoying the car you recently purchased! Is there anything you need related to the car?User has recently made a large, unique purchase

Let's say once this settles in, there is a new requirement to greet users who have not made a purchase in a while nudging the users to buy something. In the Scenario based style, we would need to create a whole new scenario. In the style with a table of results to verify against we would simply update the greeting and the reason-meta to explain why the greeting is not default.

dateusergreetingreason-meta
2021-02-17bobHello, bob! Would you like me to add coffee to your cart?bob purchased coffee mid-month each month and has not purchased coffee yet this month
2021-02-17maryHello, mary!default greeting
2021-02-17patHello, pat!default greeting
2021-03-17bobHello, bob! Would you like me to add coffee to your cart?User regularly purchases an item
2021-03-17maryHello, mary! Won't you please consider making a purchase today?User hasn't made a purchase in a few months.
2021-03-17patHello, pat! I hope you are enjoying the car you recently purchased! Is there anything you need related to the car?User has recently made a large, unique purchase



In this case it is much easier to understand the full range of choices when reading a table of values than a verbose set of scenarios which will take up more than one page of space with the GWT statements. Further more, this latter style will be easier to implement and should not require as many changes when requirements change. Simple loop through the results and compare against the table of expected values.



The choice to use each style depends on the case. Use whichever makes you write less code in the end, that will be the more elegant solution.

No comments:

Post a Comment