Last week 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.
Come back next week for a new challenge!