.. 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 severaal 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 preferrably 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 , , 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 And I add Then I should get 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:**.