gnatbdd/docs/features.rst

233 lines
9.0 KiB
ReStructuredText

.. highlight:: gherkin
********
Features
********
An application's requirements are organized into features. Such a feature has a
name (for instance one feature we would use for GNATbdd itself would be "Find
features to run"), as well as a textual description. This description is only
meant for human readers, and is not used by the automatic tool.
The features are then further divided into one or more scenarios, which are
sometimes also called "tests". These will be discussed in the next section.
Syntax of the features file
===========================
The syntax of the features file is based on the one from Cucumber, which is
a BDD standard tool in the Ruby world. The grammar and syntax in those files
is voluntarily informal, since they are meant to be editable and readable
by people other than software developers.
As a result, this documentation is mostly based on examples. Let's start
with the first general layout for a file::
Feature: name of feature
high-level description
of what the feature does (plain English, not used by the tools)
Scenario: name of scenario
Given some precondition
And some other precondition
When I do some action
And I do some other action
Then I should see a specific result
And I see something else
But I do not see something else
There could be several **scenarios** within the **feature**. In fact, there
could also be several features in a given file, but this is not recommended in
general (this is an extension of the format used by cucumber).
The scenario is split into several **steps**, each of which start on a separate
line. The steps are introduced by one of several keywords, all shown in bold
in the example above. You can use any of the keywords for any of the steps, but
in general they have the following semantic:
* **Given** puts the system in a known state, before the user starts
interacting with it. Avoid talking about user interaction in givens. These
are similar to preconditions in programming languages.
For instance, a given would setup a database, log in a user, and so on.
* **When** describes the actions performed by the user, like clicking on
elements, providing input, and so on.
* **Then** observes the outcome of the actions. These observations should be
related to the business benefit that was described in the feature. The
observation should be on some kind of output that is something that comes out
of the system (report, user interface, message,...) and preferably not
something deeply buried in the system (use unit tests for those instead).
The example above indents each level of the description. This is not
strictly mandatory, but helps make the file more readable.
The keywords are case-sensitive, as is done in other BDD tools.
Comments
--------
A feature file can contain comments anywhere. These are lines whose first
non-blank character is '#'. The comment extends to the end of the line.
Tagging
-------
Features and scenarios can be tagged with one or more tags. These tags are
specific to your application and usage of GNATbdd. Primarily, they can be used
to run subsets of the whole set of scenarios. Here is an example::
@gui @editor @req-1-1
Feature: Opening an editor restores the previous location
@startup
Scenario: Restore open editors and their location on startup
Given a previous run that was editing foo.adb at line 5
When I start the application
Then I should see a window foo.adb at line 5
The tags of the feature automatically apply to its Scenarios
Other usage of tags could be to identify *slow tests* (with @slow) so that
their timeout is increased.
A tag can also be used to link a scenario to a *high-level requirement* in your
application, for instance @HLR-12-2.
Tags can also be used to identify *expected failures* (for instance @xfail), or
*work in progress* (for instance @wip).
Step configuration
------------------
Steps describe the actual actions to perform on the software, its input or its
output. In the examples above, we have seen various sentences used to describe
those actions. However, if we have to write a different sentence for every
little variation, this will end up being very difficult to maintain indeed.
So instead, the steps can be configured so that they apply to a wide variety of
scenario. For instance, going back the example on the editors above, there is
nothing specific in the test about the name *foo.adb* or the line *5*. We might
want to rerun a similar step on file *bar.adb* at line *10*. As we will see
when we discuss the definition of steps, this is of course doable.
But staying closer to the topic of the syntax, there are two other ways that
the steps can be configured, namely **multi-line strings** and **tables**.
* multi-line strings are convenient when the text to substitute contains
several lines. They can only be used as the last part of the step, as in
the following example::
Feature: Entering multiple lines of text in the editor
Scenario: Pressing the return key on the keyboaard
Given a blank editor
When I press the keys <a>, <enter>, <b>
Then the editor should contain
"""
a
b
"""
A multi-line string starts on a line of its own just after the step itself.
It starts with three double quotes (this is a notation that is familiar to
all Python developers), and ends on a similar line that contains double-quotes.
The double-quotes must appear on a line of their own.
We recommend indenting the quotes and their contains relatively to the step
itself to improve readability.
The lines between the quotes form the text that is used for the step itself.
Those lines are unindented by an amount equal to the indentation of the first
quotes line (so in the example above there will in fact be no whitespace
before 'a' and 'b' when we compare them to the actual output). If a line does
not start with enough white spaces, GNATbdd simply removes all leading white
spaces, but preserves the first non-white character.
* tables are another great way to provide input. They organize their data into
columns, which are interpreted by the step as it sees fit. Here an example::
Feature: Logging in on a website
Scenario: Logging with valid user account
Given the following users exist
| Name | Email | Phone |
| John | john@example.com | 1234 |
| Jack | jack@example.com | 5678 |
When I log in as "Jack"
Then I should see the home page
Leading and trailing spaces are ignored for each cell in the table.
Background scenario
-------------------
The givens in the last scenario above (providing the name of multiple users for
a web site) would need to be duplicated if we wanted another scenario that tests
logging in with an invalid user. Obviously, duplication is just as bad in tests
as it is in the code itself.
Instead, you can defined a background for the feature. It defines steps to be
performed before running each of the step in the scenario. For instance, the
feature above would be better written as::
Feature: Logging in on a website
Background:
Given the following users exist
| Name | Email | Phone |
| John | john@example.com | 1234 |
| Jack | jack@example.com | 5678 |
Scenario: Logging with valid user account
When I log in as "Jack"
Then I should see the home page
Scenario: Logging with invalid user account
When I log in as "Henry"
Then I should see the login page
The background must be defined before any scenario.
Scenario outlines
-----------------
We mentioned before that parts of the steps can be configured. For instance, we
could have a feature with the following two scenarios::
Feature: Testing addition in a calculator
Scenario: adding simple numbers
When I enter 5
And I add 12
Then I should get 17
Scenario: adding larger numbers
When I enter 105
And I add 1012
Then I should get 1117
The two scenarios are very similar, this is another case of duplication that
would best be avoided.
The feature file provides the notion of a **Scenario Outline**, which provides
text substitution to create multiple scenarios. Here is the example above
rewritten by taking advantage of this feature::
Feature: Testing addition in a calculator
Scenario Outline: adding simple numbers
When I enter <num1>
And I add <num2>
Then I should get <result>
Examples:
| num1 | num2 | result |
| 5 | 12 | 17 |
| 105 | 1012 | 1117 |
The **Examples** provide the values to substitute in the steps above. There
will be one scenario executed for each line in the examples.
For compatibility with other tools, the keyword **Examples:** can be replaced
with **Scenarios:**.