#GivenWhenThenWithStyle

How to describe that a user should not be able to do something?

Many online examples of Given-When-Then files show how a user performs an action, or how some process works. Describing the opposite is sometimes a challenge. Showing that a user cannot perform an action, or that some process is blocked, is often tricky because it involves several different perspectives. Our next challenge deals with one such case:

How would you specify that a single user cannot reserve more than 10 items? In particular, how would you show the impact of that rule on the user interface.

Here is the challenge

A trivial solution for this problem would be to show a scenario outline involving the number of items and whether the reservation is allowed or not, but that shows the business rule, not the user interface. Here are some options that try to capture the user interface layer:

Option 1: Describe the visual state transitions

Given the user has reserved <previous> items 
When the user adds <new> items to reservation
Then the "Reserve" button should be <button state>

Examples:

| previous | new  | button state |
| 0        |   9  | enabled      |
| 0        |   10 | disabled     |
| 1        |   9  | disabled     |
| 8        |   1  | enabled      |
| 8        |   2  | disabled     |

Option 2: Describe the user interaction

Given the user is trying to reserve <items> items
When the user clicks the "Reserve" button
Then the action should be <action>

Examples:

| items | action                       |
| 1     | form submitted, page reloads |
| 9     | form submitted, page reloads |
| 10    | nothing happens              |
| 11    | nothing happens              |

Option 3: Describe UI-backend interaction

Given the user is trying to reserve <items> items
When the user submits the reservation form
Then the submission is <sending status>

Examples:

| items | sending status |
| 1     | sent           |
| 9     | sent           |
| 10    | blocked        |
| 11    | blocked        |

Option 4: Describe reservation and visual impact separately

Scenario: Validating reservations

Given the user tries to reserve <items> items
Then the reservation status should be <reservation state>

Examples:

| items   | reservation state |
|     1   |             valid |
|     9   |             valid |
|    10   |           invalid |
|    11   |           invalid |

Scenario: UI impact

Given the reservation status is <reservation state> 
Then the "Reserve" button should be <button state>
And the form submission should be <submission state>

Examples:

| reservation state | button state | submission state |
| valid             | enabled      | not blocked      |
| invalid           | disabled     | blocked          |

Solving: How to describe that a user should not be able to do something?

Last time, you voted for ways of describing a blocked process, such as a user not being allowed to order more than 10 items. In particular, the challenge was capturing the user interface state or interactions.

The option “Describe the visual state transitions” got the most votes, by far. In order to easily discuss it so you do not have to click links around, here is the winner according to community votes:

Given the user has reserved <previous> items 
When the user adds <new> items to reservation
Then the "Reserve" button should be <button state>

Examples:

| previous | new  | button state |
| 0        |   9  | enabled      |
| 0        |   10 | disabled     |
| 1        |   9  | disabled     |
| 8        |   1  | enabled      |
| 8        |   2  | disabled     |

The main issue with this option is that it essentially captures the process of testing, not the purpose of a test. (It describes “how”, not “what”). That’s a topic that we’re frequently revisiting in this challenge, since it’s one of the most fundamental ways of creating specs and tests that provide long-term value. Capturing the process of testing, rather than the purpose of the test, would make it difficult to use and maintain this spec in the future.

For start, there might be other reasons the reservation is not allowed. For example, the user does not have a valid payment method. In that case, adding 9 items to an empty reservation will not change the button state, but it will not be clear why. Of course, we can start capturing all that contextual information in the scenario, but it will get progressively more complex.

Also, there might be other things that need to happen if a reservation is not allowed. For example, we might want to block the form submission (as in the option 4 of the original post). There’s not much point disabling a button visually, if the users can press enter on a text field and proceed with the reservation.

Finally, there might be other things that need to happen to the button. Maybe we should hide it completely if a user is not allowed to edit the reservation, for example after it has been submitted.

Option 4 (from the challenge post) tries to split the concerns into a reservation state and the user interface impact. For a situation where there are only two possible states (valid/invalid) and a small set of impacts (button state, form action) this might be OK. But this approach doesn’t scale. Add a few more UI elements and one or two more things that can impact the reservation state, and a spec like this would get too complex.

Find the missing model concepts

One good approach to solve similar problems is to look for the missing pieces of the model. In option 1, we tie the number of items to a visual state. In option 4, we introduce the reservation validity between the number of items and the visual aspects of the system, which helps but does not scale. With a more slightly complex situation it would lead to an explosion of examples. That means we’re missing another piece of the puzzle. Going back to the original challenge topic, none of these terms describe directly that a user is allowed or not allowed to do something. We’re missing some way to capture the allowed actions in the model.

I would try to split this into three separate specifications (or at least three scenarios in the same feature file).

1. The first would deal with the reservation state.

We know the reservation can be valid/invalid based on the number of items, but perhaps there’s more to investigate here. Once a reservation is submitted, should people be allowed to add more items to it? How about when it’s processed? Will the number of items always stay 10 for everyone, or do we expect to later have users that have more or less capacity? Even if we do not have to deal with these kinds of rules now, it’s reasonable to expect more complexity to come in the future here, so it’s good to split it out.

Feature: Reservations

Background: 

Given the following capacity limits
| user type | items |
| regular   |   10  |
| premium   |   50  |

Scenario: Item quanitity validation

Given a user of type <type> creates a reservation
When the user attempts to reserve <items> items
Then the reservation status should be <status>

Examples:

| type    | items   |   status |
| regular |     1   |   valid  |
| regular |     9   |   valid  |
| regular |    10   | invalid  |
| premium |    10   |   valid  |
| premium |    49   |   valid  |
| premium |    50   |  invalid |

2. The second specification would deal with allowed actions.

Starting from the state of the reservation, it can specify whether a user is allowed to submit a reservation, to modify it, to retrieve it and so on. This is the missing piece of the puzzle. Separating it out would allow us to later introduce additional conditions, such as the account balance, that would affect users being allowed to perform actions without making things too complex.

Feature: Allowed actions

Scenario: active users 

Given a user with an active payment method
And the user has a <status> reservation
Then the user should be allowed to perform <actions>

| status   |  actions                    |
| invalid  |  view, edit, cancel,        | 
| valid    |  view, edit, cancel, submit | 
| pending  |  view, cancel               | 
| active   |  view, cancel, close        | 
| closed   |  view                       | 

3. The third part can then start from the allowed actions, and focus on individual UI elements.

We can have one scenario for the impact on the reservation button, another scenario for the impact on input fields, and a third for the impact on the form. As we add more complexity to the user interface, we just need to add another small focused scenario.

Feature: Reservation UI

Scenario: reservation edit button status 

Given a <reservation status> reservation
When the user opens the reservation screen
Then the edit button should be <button state>

| reservation status | button state |
| invalid            | disabled     |
| valid              | active       |
| pending            | hidden       |
| active             | hidden       |
| closed             | hidden       |

Scenario: reservation edit form submission

Given a <reservation status> reservation
When the user submits the reservation edit form
Then the action should be <form action>

| reservation status | form action |
| invalid            | blocked     |
| valid              | submitted   |
| pending            | blocked     |
| active             | blocked     |
| closed             | blocked     |

The reason I’d prefer to split this into three different specifications, rather than keep it all in a single feature file, is that the first and the second part do not really depend on any specific user interface.

If we threw away one UI layer and replaced it with another, the third part would become irrelevant, but the first two parts would still stay the same. If a user is not allowed to modify a submitted reservation through the web application, they should not be allowed to do that over the mobile application as well, neither through an API.

Splitting the concerns in this way allows us to reuse the same feature files across different layers. It also allows us to decide where would be best to place this risk. Maybe we should have the same rules in the UI and the back end for quick validation. On the other hand, maybe the allowed action calculator should become its own API, separate from the user interface. Separating concerns in this way allows us to discuss those options more easily.