Soe Moe
Soe Moe

Reputation: 3438

Valid BDD Scenario Steps? Given -> When -> Then -> When

If I have the following steps defined, is this valid scenario? I feel like it is some kind of smell.

Scenario: Change users status
   Given I have the following users exist:
        | code | status   |
        | u1   | active   |
        | u2   | inactive |
        | u3   | active   |
     And the status filter is "active"
    When I update "u1" to "inactive" 
    Then I should see the following users:
        | code |
        | u3   |
    When I change status filter to "inactive"
    Then I should see the following users:
        | code |
        | u1   |
        | u2   |

Upvotes: 1

Views: 1892

Answers (4)

Liberty Lover
Liberty Lover

Reputation: 992

Overall

BDD is really Design-by-Contract using different terms. Generally speaking, BDD is in the form of Given-When-Then, which is roughly analogous to Preconditions (Given), Check-conditions/Loop-invariants (When), and Post-conditions/Invariants (Then).

Notice

Note that BDD is very much Hoare-logic (i.e. {P}C{Q} or {P}recondition-[C]ommand-{Q}Post-condition). Therefore:

  • Preconditions (Given) must hold true for the command (method/function) to compute correctly. Any violation of the Given (precondition) signals a fault in the calling Client code.
  • Command(s) (When) are what happens after the precondition(s) are met. In Eiffel, they can be punctuated within the method or function code with other contracts. Think about these as though they are QA/QC checks along a process assembly line.
  • Post-conditions (Then) must hold true once the Command (When) is finished.

Moral of the Story

Because BDD is just DbC (Hoare-logic) repackaged in different words, this means it is not TDD. Why? Because TDD is not about preconditions/checks/post-condition contracts tied directly to methods, functions, properties, and class-state. TDD is the next step up the ladder in testing methods, functions, properties, and classes with their discrete states. Once you see this and fully appreciate that TDD is not BDD and BDD is not TDD, but that they are separate and complementary technologies for software correctness proofs—THEN—you will finally understand these topics correctly. You will also use and apply them correctly.

Conclusion

Eiffel is the only language I am aware of where BDD (Design-by-Contract) is baked raw into both the language specification and compiler. It is not a Frankenstein bolt-on monster with limitations. In Eiffel, BDD (aka DbC) is an elegant, helpful, useful, and direct participant in the software correctness toolbox.

See Also

Wikipedia helps defined Hoare-logic. See: https://en.wikipedia.org/wiki/Hoare_logic

I have created an example in Eiffel that you can look at. See:

Primary class: https://github.com/ljr1981/stack_overflow_answers/blob/main/src/so_73347395/so_73347395.e

Test class: https://github.com/ljr1981/stack_overflow_answers/blob/main/testing/so_73347395/so_73347395_test_set.e

Upvotes: 0

Jan Schaefer
Jan Schaefer

Reputation: 1932

Typically it is good to just have one 'when' step, because that is what you actually test. However, sometimes I find it also useful to specify whole use cases that might include several then and when steps that depend on each other. For example:

 when a new user registers
 then the user receives an email confirmation
 when the email confirmation is confirmed by the user
 then the user is registered 

In your example, however, you really should write two tests, because you actually test two different features that also do not directly depend on each other.

Upvotes: 0

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236188

Liz is right about describing capabilities of system. I would also suggest to have only one When in scenario. Because each scenario (I think) should verify that system goes from Given state to Then state When something happens.

In this case I also suggest to have two different features. One feature is about your application can change user status (activate/deactivate):

Feature: Changing user status

Scenario: Deactivating user
   Given following users exist:
        | code | status   |
        | u1   | active   |
        | u2   | inactive |
        | u3   | active   |
    When user u1 is deactivated
    Then following users exist:
        | code | status   |
        | u1   | inactive |
        | u2   | inactive |
        | u3   | active   |

Another feature is about you can filter users by status:

Feature: Filtering users

Scenario: Filtering active users
   Given following users exist:
        | code | status   |
        | u1   | active   |
        | u2   | inactive |
        | u3   | active   |
    When I filter for active users
    Then I should see the following users:
        | code |
        | u1   |
        | u3   |

Scenario: Filtering inactive users
   Given following users exist:
        | code | status   |
        | u1   | active   |
        | u2   | inactive |
        | u3   | active   |
    When I filter for inactive users
    Then I should see the following users:
        | code |
        | u2   |

In this case when one of the scenarios is broken, you will know the reason. It's either not changing status of user, or not filtering them properly. You know which feature is broken in your application.

Upvotes: 6

Lunivore
Lunivore

Reputation: 17602

You're describing it in quite code-driven terms, but otherwise it's a valid scenario.

If you wanted to make it better, you could describe it in terms of the capabilities of the system, in the language that users might use to describe what they're doing:

Scenario: Change users status
   Given these users exist:
        | code | status   |
        | u1   | active   |
        | u2   | inactive |
        | u3   | active   |
    And we filter for active users
    When I disable user u1
    Then I should see the following users:
        | code |
        | u3   |
    When we filter for inactive users
    Then I should see the following users:
        | code |
        | u1   |
        | u2   |

You could also use more typical usernames so that people reading it understand what those names represent at a glance (Lunivore, Soe, Jon instead of u1 and u2).

Not so much difference. What did you identify as a bad smell? Was it just the language?

Upvotes: 4

Related Questions