From 0166799e65c52c1d783415155a0ebd48ea32be4a Mon Sep 17 00:00:00 2001 From: Javix Date: Sun, 3 Jul 2016 22:41:16 +0200 Subject: [PATCH 1/8] Add Gherkin topic * create gherkin.md * add feature introduction section * add step definitions section --- docs/gherkin.md | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 docs/gherkin.md diff --git a/docs/gherkin.md b/docs/gherkin.md new file mode 100644 index 00000000000..f1a4d7b7ca6 --- /dev/null +++ b/docs/gherkin.md @@ -0,0 +1,112 @@ +# Gherkin + +Gherkin is the language that Cucumber understands. It is a Business Readable, [Domain Specific Language](http://martinfowler.com/bliki/BusinessReadableDSL.html) that lets you describe software’s behaviour without detailing how that behaviour is implemented. + +Gherkin serves two purposes — documentation and automated tests. The third is a bonus feature — when it yells in red it’s talking to you, telling you what code you should write. + +Gherkin’s grammar is defined in the Treetop grammar that is part of the Cucumber codebase. The grammar exists in different flavours for many spoken languages (37 at the time of writing), so that your team can use the keywords in your own language. + +There are a few conventions. + + * Single Gherkin source file contains a description of a single feature. + * Source files have `.feature` extension. + +## Gherkin Syntax + +Like Python and YAML, Gherkin is a line-oriented language that uses indentation to define structure. Line endings terminate statements (eg, steps). Either spaces or tabs may be used for indentation (but spaces are more portable). Most lines start with a keyword. + +Comment lines are allowed anywhere in the file. They begin with zero or more spaces, followed by a hash sign (`#`) and some amount of text. + +The parser divides the input into features, scenarios and steps. When you run the feature the trailing portion (after the keyword) of each step is matched to a Ruby code block called [Step Definitions](docs/step-definitions.md). + +A Gherkin source file usually looks like this +``` +1: Feature: Some terse yet descriptive text of what is desired + 2: Textual description of the business value of this feature + 3: Business rules that govern the scope of the feature + 4: Any additional information that will make the feature easier to understand + 5: + 6: Scenario: Some determinable business situation + 7: Given some precondition + 8: And some other precondition + 9: When some action by the actor +10: And some other action +11: And yet another action +12: Then some testable outcome is achieved +13: And something else we can check happens too +14: +15: Scenario: A different situation +16: ... +``` + +First line starts the feature. Lines 2–4 are unparsed text, which is expected to describe the business value of this feature. Line 6 starts a scenario. Lines 7–13 are the steps for the scenario. Line 15 starts next scenario and so on. + +Read more + + * [Feature Introduction](docs/gherkin.md#feature) – general structure of a feature + * [Given/When/Then (Steps)](docs/gherkin.md#steps) + +## Feature Introduction {#feature} + +Every `.feature` file conventionally consists of a single feature. A line starting with the keyword Feature followed by free indented text starts a feature. A feature usually contains a list of scenarios. You can write whatever you want up until the first scenario, which starts with the word Scenario (or localized equivalent; Gherkin is localized for [dozens of languages](https://github.com/cucumber/cucumber/wiki/Spoken-languages)) on a new line. You can use tagging to group features and scenarios together independent of your file and directory structure. + +Every scenario consists of a list of steps, which must start with one of the keywords **Given, When, Then, But** or **And**. Cucumber treats them all the same, but you shouldn’t. Here is an example: + +``` +Feature: Serve coffee + Coffee should not be served until paid for + Coffee should not be served until the button has been pressed + If there is no coffee left then money should be refunded + + Scenario: Buy last coffee + Given there are 1 coffees left in the machine + And I have deposited 1$ + When I press the coffee button + Then I should be served a coffee +``` + +In addition to a scenario, a feature may contain a background, scenario outline and examples. Respective keywords (in English) and places to read more about them are listed below. You can get a list of localized keywords with cucumber `--i18n [LANG]`. + +| keyword | localized | more info see | +|-----------------|-------------------------|-------------------| +| name | 'English' | | +| native | 'English' | | +| encoding | 'UTF-8' | | +| feature | 'Feature' | [Feature](docs/gherkin.md#feature) +| background | 'Backgroound' | [Background](docs/gherkin.md#background) +| scenario | 'Scenario' | [Scenario](docs/gherkin.md#feature) +| scenario_outline| 'Scenario Outline' | [Scenario Outline](docs/gherkin.md#background) +| examples | 'Examples'/ 'Scenarios' | [Scenario Outline](docs/gherkin.md#background) +| given | 'Given' | [Given/When/Then (Steps)](docs/gherkin.md#steps) +| when | 'When' | [Given/When/Then (Steps)](docs/gherkin.md#steps) +| then | 'Then' | [Given/When/Then (Steps)](docs/gherkin.md#steps) +| and | 'And' | [Given/When/Then (Steps)](docs/gherkin.md#steps) +| but | 'But' | [Given/When/Then (Steps)](docs/gherkin.md#steps) + +## Step definitions {#steps} + +For each step Cucumber will look for a matching **step definition**. A step definition is written in Ruby. Each step definition consists of a keyword, a string or regular expression, and a block. Example: + +``` +# features/step_definitions/coffee_steps.rb + +Then "I should be served coffee" do + @machine.dispensed_drink.should == "coffee" +end +``` + +Step definitions can also take parameters if you use regular expressions: + +``` +# features/step_definitions/coffee_steps.rb + +Given /there are (\d+) coffees left in the machine/ do |n| + @machine = Machine.new(n.to_i) +end +``` + +This step definition uses a regular expression with one match group – `(\d+)`. (It matches any sequence of digits). Therefore, it matches the first line of the scenario. The value of each matched group gets yielded to the block as a string. You must take care to have the same number of regular expression groups and block arguments. Since block arguments are always strings, you have to do any type conversions inside the block, or use Step Argument Transforms. + +When Cucumber prints the results of the running features it will underline all step arguments so that it’s easier to see what part of a step was actually recognised as an argument. It will also print the path and line of the matching step definition. This makes it easy to go from a feature file to any step definition. + +Take a look at [Step Definitions](docs/step-definitions.md) and the examples directory to see more. From 2e01622e942460f01d56a01bd93c73f94ae39624 Mon Sep 17 00:00:00 2001 From: Javix Date: Wed, 6 Jul 2016 21:09:47 +0200 Subject: [PATCH 2/8] Add new sections and topics * add hooks * add doc strings * add background * add comments * update SUMMARY links --- SUMMARY.md | 5 +-- docs/gherkin.md | 100 ++++++++++++++++++++++++++++++++++++++++++++++-- docs/hooks.md | 83 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 7 deletions(-) create mode 100644 docs/hooks.md diff --git a/SUMMARY.md b/SUMMARY.md index 7dff722787b..1ffb305ad8f 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -70,10 +70,9 @@ * [Tags](docs/tags.md) * [Background](docs/gherkin.md#background) * [Scenario Outline](docs/gherkin.md#background) - * [Background](gdocs/herkin.md#background) * [Data Tables](docs/gherkin.md#background) - * [Doc Strings](docs/gherkin.md#background) - * [Comments](#) + * [Doc Strings](docs/gherkin.md#doc-strings) + * [Comments](docs/gherkin.md#comments) * [Internationalisation](#) * [Step Definitions](docs/step-definitions.md) * [Regular Expressions](#) diff --git a/docs/gherkin.md b/docs/gherkin.md index f1a4d7b7ca6..dbc9f390c4e 100644 --- a/docs/gherkin.md +++ b/docs/gherkin.md @@ -72,10 +72,10 @@ In addition to a scenario, a feature may contain a background, scenario outline | name | 'English' | | | native | 'English' | | | encoding | 'UTF-8' | | -| feature | 'Feature' | [Feature](docs/gherkin.md#feature) -| background | 'Backgroound' | [Background](docs/gherkin.md#background) -| scenario | 'Scenario' | [Scenario](docs/gherkin.md#feature) -| scenario_outline| 'Scenario Outline' | [Scenario Outline](docs/gherkin.md#background) +| feature | 'Feature' | [Feature](gherkin.md#feature) +| background | 'Backgroound' | [Background](gherkin.md#background) +| scenario | 'Scenario' | [Scenario](gherkin.md#feature) +| scenario_outline| 'Scenario Outline' | [Scenario Outline](gherkin.md#background) | examples | 'Examples'/ 'Scenarios' | [Scenario Outline](docs/gherkin.md#background) | given | 'Given' | [Given/When/Then (Steps)](docs/gherkin.md#steps) | when | 'When' | [Given/When/Then (Steps)](docs/gherkin.md#steps) @@ -110,3 +110,95 @@ This step definition uses a regular expression with one match group – `(\d+)`. When Cucumber prints the results of the running features it will underline all step arguments so that it’s easier to see what part of a step was actually recognised as an argument. It will also print the path and line of the matching step definition. This makes it easy to go from a feature file to any step definition. Take a look at [Step Definitions](docs/step-definitions.md) and the examples directory to see more. + +## Background {#background} + +A background section in a feature file allows you to specify a set of steps that are common to every scenario in the file. Instead of having to repeat those steps over and over for each scenario, you move them up into a Background element. There are a couple of advantages to doing this: + +A Background is much like a scenario containing a number of steps. The difference is when it is run. The background is run before each of your scenarios but after any of your Before [Hooks](hooks.md). + +Example: + +``` +Feature: Change PIN + #some feature description comes here + + Background: + Given I have been issued a new card + And I insert the card, entering the correct PIN + And I choose "Change PIN" from the menu + + Scenario: Change PIN successfully + When I change the PIN to 9876 + Then the system should remember my PIN is now 9876 + + Scenario: Try to change PIN to the same as before + When I try to change the PIN to the original PIN number + Then I should see a warning message + And the system should not have changed my PIN +``` + +You can have a single `Background` element per feature file, and it must appear before any of the `Scenario` or `Scenario Outline` elements. Just like all the other _Gherkin_ elements, you can give it a name, and you have space to put a multiline description before the first step. For example: + +``` +Feature: Change PIN + In order to be able to change it to something they can easily + remember, customers with new bank cards need to be able to + change their PIN using the ATM. + + Background: Insert a newly issued card and sign in + Whenever the bank issues new cards to customers, they are supplied + with a Personal Identification Number (PIN) that is randomly + generated by the system. + + Given I have been issued a new card + And I insert the card, entering the correct PIN + ... +``` + +### Good practices for using Background + + * Don’t use `Background` to set up complicated state unless that state is actually something the client needs to know. + * Keep your `Background` section short. + * Make your `Background` section vivid. + * Keep your scenarios short and don’t have too many. + +## Doc Strings {#doc-strings} + +Doc strings just allow you to specify a larger piece of text than you could fit on a single line. For example, if you need to describe the precise content of an email message, you could do it like this: + +``` +Scenario: Ban Unscrupulous Users + When I behave unscrupulously + Then I should receive an email containing: + """ + Dear Sir, + Your account privileges have been revoked due to your unscrupulous behavior. + Sincerely, + The Management + """ + And my account should be locked +``` + +Just like a [Data Tables](docs/gherkin.md#datatable), the entire string between the `"""` triple quotes is attached to the step above it. The indentation of the opening `"""` is not important, although common practice is to indent two spaces in from the enclosing step, as shown. + +The indentation inside the triple quotes, however, is significant: imagine the left margin running down from the start of the first """. If you want to include indentation within your string, you need to indent it within this margin. + +## Comments {#comments} + +As well as the description fields that follow Feature and Scenario keywords, Cucumber allows you to precede these keywords with comments. +Like in Ruby, comments start with a `#` character. Unlike in Ruby, comments have to be the first and the only thing on a line (well, apart from whitespace). + +Here’s an example: +``` +# This feature covers the account transaction and hardware-driver modules +Feature: Withdraw Cash + In order to buy beer + As an account holder + I want to withdraw cash from the ATM + + # Can't figure out how to integrate with magic wand interface + Scenario: Withdraw too much from an account in credit + ... +``` + diff --git a/docs/hooks.md b/docs/hooks.md new file mode 100644 index 00000000000..9fb80bf66ce --- /dev/null +++ b/docs/hooks.md @@ -0,0 +1,83 @@ +# Hooks + +Cucumber provides a number of hooks which allow us to run blocks at various points in the Cucumber test cycle. You can define them anywhere in your support or step definition layers, using the methods `Before` and `After`. Hooks are global by default, meaning they run for every scenario in your +features. If you want them to run for just certain scenarios, you need to tag +those scenarios and then use a [tagged hook](hooks.md#tagged-hooks). + +## Scenario hooks + +`Before` hooks will be run before the first step of each scenario. They will run in the same order of which they are registered. + +``` +Before do + # Do something before each scenario. +end +``` + +or like this: + +``` +Before do |scenario| + # The +scenario+ argument is optional, but if you use it, you can get the title, + # description, or name (title + description) of the scenario that is about to be + # executed. + Rails.logger.debug "Starting scenario: #{scenario.title}" +end +``` + +`After` hooks will be run after the last step of each scenario, even when there are failing, undefined, pending or skipped steps. They will run in the opposite order of which they are registered. + +``` +After do |scenario| + # Do something after each scenario. + # The +scenario+ argument is optional, but + # if you use it, you can inspect status with + # the #failed?, #passed? and #exception methods. + + if scenario.failed? + subject = "[Project X] #{scenario.exception.message}" + send_failure_email(subject) + end +end +``` + +`Around` hooks give you more power than Before and After hooks, allowing you to +actually control how many times the scenario is run. An Around hook is passed +two parameters: an object that represents the scenario and a block of code +that, when called, will run the scenario: + +``` +Around do |scenario, block| + puts "About to run #{scenario.name}" + block.call + puts "Finished running #{scenario.name}" +end +``` + +`Around` hooks also accept tags, so you can restrict them to run for only some +scenarios, just like `Before` and `After` hooks. One great trick we’ve seen them used for is to run the same scenario twice under different conditions, like this: + +``` +Around('@run_with_and_without_javascript') do |scenario, block| +Capybara.current_driver = Capybara.javascript_driver + block.call + Capybara.use_default_driver + block.call +end +``` + +## Tagged hooks {#tagged-hooks} + +Both `Before` and `After` accept a tag expression, which you can use to selectively add the hook to only certain scenarios. + +``` +@admin +Feature: Delete Widgets +... + Before('@admin') do + user = create_user(:admin => true) + login_as user + end +``` + +Now, to run a scenario when you need to be logged in as an administrator, you just have to tag the scenario with `@admin`, and this code will automatically run before the steps of the scenario. From e5f226138ea00607c7b9427bd2919f8550fb8de3 Mon Sep 17 00:00:00 2001 From: Javix Date: Wed, 6 Jul 2016 21:22:11 +0200 Subject: [PATCH 3/8] Add tags topic * add tags.md --- docs/tags.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/tags.md diff --git a/docs/tags.md b/docs/tags.md new file mode 100644 index 00000000000..901f7d0338e --- /dev/null +++ b/docs/tags.md @@ -0,0 +1,31 @@ +# Tags + +Tags are a way to group Scenarios. They are `@`-prefixed strings and you can place as many tags as you like above `Feature, Scenario, Scenario Outline` or `Examples` keywords. Space character are invalid in tags and may separate them. + +Tags are inherited from parent elements. For example, if you place a tag above a `Feature`, all scenarios in that feature will get that tag. + +If you want to tag all the scenarios in a feature at once, just tag the `Feature` element at the top, and all the scenarios will inherit the tag. You can still tag individual scenarios as well. + +Similarly, if you place a tag above a `Scenario Outline` or `Examples` keyword, all scenarios derived from examples rows will inherit the tags. + +A `Scenario` or `Feature` can have as many tags as you like. Just separate them with spaces: + +``` +@billing @bicker @annoy +Feature: Verify billing +``` + +You can tell Cucumber to only run scenarios with certain tags, or to exclude scenarios with certain tags. + +Cucumber can perform different operations before and after each scenario based on what tags are present on a scenario. + +There are three main reasons for tagging scenarios: + + * _Documentation_: You simply want to use a tag to attach a label to certain + scenarios, for example to label them with an ID from a project management + tool. + * _Filtering_: Cucumber allows you to use tags as a filter to pick out specific scenarios to run or report on. You can even have Cucumber fail your test run if a certain tag appears too many times. + * _Hooks_: Run a block of Ruby code whenever a scenario with a particular + tag is about to start or has just finished. + +See [tagged hooks](hooks.md#tagged-hooks) for more details. From a42162d27e0ff5dc237fcef20f36141f89fa4373 Mon Sep 17 00:00:00 2001 From: Javix Date: Thu, 7 Jul 2016 12:49:52 +0200 Subject: [PATCH 4/8] Update after code review * remove line numbers in code examples * drop specificities as too complicated for a beginner * make Gherkin explanation simpler --- docs/gherkin.md | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/docs/gherkin.md b/docs/gherkin.md index dbc9f390c4e..57f6c30ae19 100644 --- a/docs/gherkin.md +++ b/docs/gherkin.md @@ -4,7 +4,7 @@ Gherkin is the language that Cucumber understands. It is a Business Readable, [D Gherkin serves two purposes — documentation and automated tests. The third is a bonus feature — when it yells in red it’s talking to you, telling you what code you should write. -Gherkin’s grammar is defined in the Treetop grammar that is part of the Cucumber codebase. The grammar exists in different flavours for many spoken languages (37 at the time of writing), so that your team can use the keywords in your own language. +Gherkin’s grammar exists in different flavours for many spoken languages (37 at the time of writing), so that your team can use the keywords in your own language. There are a few conventions. @@ -13,30 +13,24 @@ There are a few conventions. ## Gherkin Syntax -Like Python and YAML, Gherkin is a line-oriented language that uses indentation to define structure. Line endings terminate statements (eg, steps). Either spaces or tabs may be used for indentation (but spaces are more portable). Most lines start with a keyword. - -Comment lines are allowed anywhere in the file. They begin with zero or more spaces, followed by a hash sign (`#`) and some amount of text. - -The parser divides the input into features, scenarios and steps. When you run the feature the trailing portion (after the keyword) of each step is matched to a Ruby code block called [Step Definitions](docs/step-definitions.md). - A Gherkin source file usually looks like this ``` -1: Feature: Some terse yet descriptive text of what is desired - 2: Textual description of the business value of this feature - 3: Business rules that govern the scope of the feature - 4: Any additional information that will make the feature easier to understand - 5: - 6: Scenario: Some determinable business situation - 7: Given some precondition - 8: And some other precondition - 9: When some action by the actor -10: And some other action -11: And yet another action -12: Then some testable outcome is achieved -13: And something else we can check happens too -14: -15: Scenario: A different situation -16: ... +Feature: Some terse yet descriptive text of what is desired + Textual description of the business value of this feature + Business rules that govern the scope of the feature + Any additional information that will make the feature easier to understand + + Scenario: Some determinable business situation + Given some precondition + And some other precondition + When some action by the actor + And some other action + And yet another action + Then some testable outcome is achieved + And something else we can check happens too + + Scenario: A different situation + ... ``` First line starts the feature. Lines 2–4 are unparsed text, which is expected to describe the business value of this feature. Line 6 starts a scenario. Lines 7–13 are the steps for the scenario. Line 15 starts next scenario and so on. From d00a4e2e781dcae863c45c2abb4772eeabffedc0 Mon Sep 17 00:00:00 2001 From: Javix Date: Thu, 7 Jul 2016 22:12:04 +0200 Subject: [PATCH 5/8] Fix code review issues --- docs/tags.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/docs/tags.md b/docs/tags.md index 901f7d0338e..30ff61de27d 100644 --- a/docs/tags.md +++ b/docs/tags.md @@ -1,6 +1,6 @@ # Tags -Tags are a way to group Scenarios. They are `@`-prefixed strings and you can place as many tags as you like above `Feature, Scenario, Scenario Outline` or `Examples` keywords. Space character are invalid in tags and may separate them. +Tags are a way to group Scenarios. They are `@`-prefixed strings and you can place as many tags as you like above `Feature`, `Scenario`, `Scenario Outline` or `Examples` keywords. Space character are invalid in tags and may separate them. Tags are inherited from parent elements. For example, if you place a tag above a `Feature`, all scenarios in that feature will get that tag. @@ -15,17 +15,12 @@ A `Scenario` or `Feature` can have as many tags as you like. Just separate them Feature: Verify billing ``` -You can tell Cucumber to only run scenarios with certain tags, or to exclude scenarios with certain tags. - Cucumber can perform different operations before and after each scenario based on what tags are present on a scenario. There are three main reasons for tagging scenarios: - * _Documentation_: You simply want to use a tag to attach a label to certain - scenarios, for example to label them with an ID from a project management - tool. - * _Filtering_: Cucumber allows you to use tags as a filter to pick out specific scenarios to run or report on. You can even have Cucumber fail your test run if a certain tag appears too many times. - * _Hooks_: Run a block of Ruby code whenever a scenario with a particular - tag is about to start or has just finished. + * _Filtering_: Cucumber allows you to use tags as a filter to pick out specific scenarios to run or report on. You can even have Cucumber fail your test run if a certain tag appears too many times. + * _Hooks_: Run a block of Ruby code whenever a scenario with a particular tag is about to start or has just finished. + * _Documentation_: You simply want to use a tag to attach a label to certain scenarios, for example to label them with an ID from a project management tool. See [tagged hooks](hooks.md#tagged-hooks) for more details. From d0e915ba11af2d65e81031c51089b4033c3263f7 Mon Sep 17 00:00:00 2001 From: Javix Date: Thu, 7 Jul 2016 23:13:54 +0200 Subject: [PATCH 6/8] Fix some broken links --- docs/gherkin.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/gherkin.md b/docs/gherkin.md index 57f6c30ae19..cdedeec35d6 100644 --- a/docs/gherkin.md +++ b/docs/gherkin.md @@ -37,8 +37,8 @@ First line starts the feature. Lines 2–4 are unparsed text, which is expected Read more - * [Feature Introduction](docs/gherkin.md#feature) – general structure of a feature - * [Given/When/Then (Steps)](docs/gherkin.md#steps) + * [Feature Introduction](gherkin.md#feature) – general structure of a feature + * [Given/When/Then (Steps)](gherkin.md#steps) ## Feature Introduction {#feature} @@ -103,7 +103,7 @@ This step definition uses a regular expression with one match group – `(\d+)`. When Cucumber prints the results of the running features it will underline all step arguments so that it’s easier to see what part of a step was actually recognised as an argument. It will also print the path and line of the matching step definition. This makes it easy to go from a feature file to any step definition. -Take a look at [Step Definitions](docs/step-definitions.md) and the examples directory to see more. +Take a look at [Step Definitions](step-definitions.md) and the examples directory to see more. ## Background {#background} From 59b18dad7816afb7c83b5c6843b21b51768f9a45 Mon Sep 17 00:00:00 2001 From: Javix Date: Fri, 8 Jul 2016 21:53:43 +0200 Subject: [PATCH 7/8] Use codetabs for Ruby and Java examples --- docs/gherkin.md | 23 +++++++++++++++++----- docs/hooks.md | 51 ++++++++++++++++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/docs/gherkin.md b/docs/gherkin.md index cdedeec35d6..26c78055a1e 100644 --- a/docs/gherkin.md +++ b/docs/gherkin.md @@ -81,23 +81,37 @@ In addition to a scenario, a feature may contain a background, scenario outline For each step Cucumber will look for a matching **step definition**. A step definition is written in Ruby. Each step definition consists of a keyword, a string or regular expression, and a block. Example: -``` +{% codetabs name="Ruby", type="rb" -%} # features/step_definitions/coffee_steps.rb Then "I should be served coffee" do @machine.dispensed_drink.should == "coffee" end -``` +{%- language name="Java", type="java" -%} +{% raw %} +@Given("I have \\$100 in my Account") +public void iHave$100InMyAccount() throws Throwable { +// TODO: code that puts $100 into User's Account goes here +} +{% endraw %} +{%- endcodetabs %} Step definitions can also take parameters if you use regular expressions: -``` +{% codetabs name="Ruby", type="rb" -%} # features/step_definitions/coffee_steps.rb Given /there are (\d+) coffees left in the machine/ do |n| @machine = Machine.new(n.to_i) end -``` +{%- language name="Java", type="java" -%} +{% raw%} +@Given("I have deposited \\$(100) in my Account") +public void iHaveDeposited$100InMyAccount(int amount) { +// TODO: code goes here +} +{% endraw %} +{%- endcodetabs %} This step definition uses a regular expression with one match group – `(\d+)`. (It matches any sequence of digits). Therefore, it matches the first line of the scenario. The value of each matched group gets yielded to the block as a string. You must take care to have the same number of regular expression groups and block arguments. Since block arguments are always strings, you have to do any type conversions inside the block, or use Step Argument Transforms. @@ -195,4 +209,3 @@ Feature: Withdraw Cash Scenario: Withdraw too much from an account in credit ... ``` - diff --git a/docs/hooks.md b/docs/hooks.md index 9fb80bf66ce..05be4979ad7 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -8,26 +8,32 @@ those scenarios and then use a [tagged hook](hooks.md#tagged-hooks). `Before` hooks will be run before the first step of each scenario. They will run in the same order of which they are registered. -``` +{% codetabs name="Ruby", type="rb" -%} Before do # Do something before each scenario. end -``` +{%- language name="Java", type="java" -%} + {% raw %} + @Before + public void beforeCallingScenario() { + System.out.println("*********** About to start the scenario."); + } + {% endraw %} +{%- endcodetabs %} or like this: -``` +{% codetabs name="Ruby", type="rb" -%} Before do |scenario| # The +scenario+ argument is optional, but if you use it, you can get the title, # description, or name (title + description) of the scenario that is about to be # executed. Rails.logger.debug "Starting scenario: #{scenario.title}" end -``` +{%- endcodetabs %} `After` hooks will be run after the last step of each scenario, even when there are failing, undefined, pending or skipped steps. They will run in the opposite order of which they are registered. - -``` +{% codetabs name="Ruby", type="rb" -%} After do |scenario| # Do something after each scenario. # The +scenario+ argument is optional, but @@ -39,38 +45,46 @@ After do |scenario| send_failure_email(subject) end end -``` +{%- language name="Java", type="java" -%} + {% raw %} + @After + public void afterRunningScenario(Scenario scenario) { + System.out.println("*********** Just finished running scenario: " + + scenario.getStatus()); + } + {% endraw %} +{%- endcodetabs %} `Around` hooks give you more power than Before and After hooks, allowing you to actually control how many times the scenario is run. An Around hook is passed two parameters: an object that represents the scenario and a block of code that, when called, will run the scenario: -``` +{% codetabs name="Ruby", type="rb" -%} Around do |scenario, block| puts "About to run #{scenario.name}" block.call puts "Finished running #{scenario.name}" end -``` +{%- endcodetabs %} `Around` hooks also accept tags, so you can restrict them to run for only some scenarios, just like `Before` and `After` hooks. One great trick we’ve seen them used for is to run the same scenario twice under different conditions, like this: -``` +{% codetabs name="Ruby", type="rb" -%} Around('@run_with_and_without_javascript') do |scenario, block| Capybara.current_driver = Capybara.javascript_driver block.call Capybara.use_default_driver block.call end -``` +{%- endcodetabs %} ## Tagged hooks {#tagged-hooks} Both `Before` and `After` accept a tag expression, which you can use to selectively add the hook to only certain scenarios. -``` +{% codetabs name="Ruby", type="rb" -%} @admin Feature: Delete Widgets ... @@ -78,6 +92,17 @@ Feature: Delete Widgets user = create_user(:admin => true) login_as user end -``` +{%- language name="Java", type="java" -%} + + @admin + Feature: Delete Widgets + ... + {% raw %} + @Before("@admin") + public void logInAsAdmin() { + // Log in as admin user + } + {% endraw %} +{%- endcodetabs %} Now, to run a scenario when you need to be logged in as an administrator, you just have to tag the scenario with `@admin`, and this code will automatically run before the steps of the scenario. From 18aa6acd9f08f5c3cf2aa261a188925c042d4857 Mon Sep 17 00:00:00 2001 From: Javix Date: Thu, 14 Jul 2016 11:08:46 +0200 Subject: [PATCH 8/8] Fix code review remarks * add gherkin highlighting * remove line numbers referring * fix inter document links syntax * bold key words separately * add the explanation for Given-When-Then blocks * capitalise keywords and link to their section * use bullet list to list key words * express steps in past tense * unindent scenarios to be at the same level as Background * change Good practices to apply not to just the Background * make a h3 section for Before and After hooks * describe the difference between Before hook and Background * make each code tab do the same thing * remove Around hooks section --- docs/gherkin.md | 123 +++++++++++++++++------------------------------- docs/hooks.md | 54 +++++++++------------ 2 files changed, 63 insertions(+), 114 deletions(-) diff --git a/docs/gherkin.md b/docs/gherkin.md index 26c78055a1e..5bde76e5046 100644 --- a/docs/gherkin.md +++ b/docs/gherkin.md @@ -14,7 +14,7 @@ There are a few conventions. ## Gherkin Syntax A Gherkin source file usually looks like this -``` +```gherkin Feature: Some terse yet descriptive text of what is desired Textual description of the business value of this feature Business rules that govern the scope of the feature @@ -22,31 +22,41 @@ Feature: Some terse yet descriptive text of what is desired Scenario: Some determinable business situation Given some precondition - And some other precondition When some action by the actor - And some other action - And yet another action Then some testable outcome is achieved - And something else we can check happens too Scenario: A different situation ... ``` - -First line starts the feature. Lines 2–4 are unparsed text, which is expected to describe the business value of this feature. Line 6 starts a scenario. Lines 7–13 are the steps for the scenario. Line 15 starts next scenario and so on. +You can probably see that Cucumber expects a little bit of structure in this file. The keywords `Feature`, `Scenario`, `Given`, `When`, and `Then` are the structure, and everything else is documentation. Read more - * [Feature Introduction](gherkin.md#feature) – general structure of a feature - * [Given/When/Then (Steps)](gherkin.md#steps) + * [Feature Introduction](#feature) – general structure of a feature + * [Given/When/Then (Steps)](#steps) ## Feature Introduction {#feature} -Every `.feature` file conventionally consists of a single feature. A line starting with the keyword Feature followed by free indented text starts a feature. A feature usually contains a list of scenarios. You can write whatever you want up until the first scenario, which starts with the word Scenario (or localized equivalent; Gherkin is localized for [dozens of languages](https://github.com/cucumber/cucumber/wiki/Spoken-languages)) on a new line. You can use tagging to group features and scenarios together independent of your file and directory structure. +Each Gherkin file begins with the `Feature` keyword. +A Cucumber feature is a grouping of related scenarios, describing the same software feature. Every `.feature` file conventionally consists of a single feature. -Every scenario consists of a list of steps, which must start with one of the keywords **Given, When, Then, But** or **And**. Cucumber treats them all the same, but you shouldn’t. Here is an example: +The text immediately following on the same line as the `Feature` keyword is the name of the feature, and the remaining lines are its description. +You can include any text you like in the description except a line beginning with one of the words `Scenario`, `Background`, or `Scenario Outline`. -``` +A feature usually contains a list of scenarios. +You can write whatever you want up until the first scenario, which starts with the word `Scenario` (or localized equivalent; Gherkin is localized for [dozens of languages](https://github.com/cucumber/cucumber/wiki/Spoken-languages)) on a new line. + +You can use tagging to group features and scenarios together independent of your file and directory structure. + +Every scenario consists of a list of steps, which must start with one of the keywords **Given**, **When**, **Then**, **But** or **And**. Cucumber treats them all the same, but you shouldn’t. + +* Given describes the current state of the world. It can also describe something that has happened in the past. +* When is an event, or an action that is expected to cause an observable change. +* Then is an expected and observable outcome, or result. + +Here is an example: + +```gherkin Feature: Serve coffee Coffee should not be served until paid for Coffee should not be served until the button has been pressed @@ -59,71 +69,22 @@ Feature: Serve coffee Then I should be served a coffee ``` -In addition to a scenario, a feature may contain a background, scenario outline and examples. Respective keywords (in English) and places to read more about them are listed below. You can get a list of localized keywords with cucumber `--i18n [LANG]`. - -| keyword | localized | more info see | -|-----------------|-------------------------|-------------------| -| name | 'English' | | -| native | 'English' | | -| encoding | 'UTF-8' | | -| feature | 'Feature' | [Feature](gherkin.md#feature) -| background | 'Backgroound' | [Background](gherkin.md#background) -| scenario | 'Scenario' | [Scenario](gherkin.md#feature) -| scenario_outline| 'Scenario Outline' | [Scenario Outline](gherkin.md#background) -| examples | 'Examples'/ 'Scenarios' | [Scenario Outline](docs/gherkin.md#background) -| given | 'Given' | [Given/When/Then (Steps)](docs/gherkin.md#steps) -| when | 'When' | [Given/When/Then (Steps)](docs/gherkin.md#steps) -| then | 'Then' | [Given/When/Then (Steps)](docs/gherkin.md#steps) -| and | 'And' | [Given/When/Then (Steps)](docs/gherkin.md#steps) -| but | 'But' | [Given/When/Then (Steps)](docs/gherkin.md#steps) - -## Step definitions {#steps} - -For each step Cucumber will look for a matching **step definition**. A step definition is written in Ruby. Each step definition consists of a keyword, a string or regular expression, and a block. Example: - -{% codetabs name="Ruby", type="rb" -%} -# features/step_definitions/coffee_steps.rb - -Then "I should be served coffee" do - @machine.dispensed_drink.should == "coffee" -end -{%- language name="Java", type="java" -%} -{% raw %} -@Given("I have \\$100 in my Account") -public void iHave$100InMyAccount() throws Throwable { -// TODO: code that puts $100 into User's Account goes here -} -{% endraw %} -{%- endcodetabs %} - -Step definitions can also take parameters if you use regular expressions: - -{% codetabs name="Ruby", type="rb" -%} -# features/step_definitions/coffee_steps.rb - -Given /there are (\d+) coffees left in the machine/ do |n| - @machine = Machine.new(n.to_i) -end -{%- language name="Java", type="java" -%} -{% raw%} -@Given("I have deposited \\$(100) in my Account") -public void iHaveDeposited$100InMyAccount(int amount) { -// TODO: code goes here -} -{% endraw %} -{%- endcodetabs %} - -This step definition uses a regular expression with one match group – `(\d+)`. (It matches any sequence of digits). Therefore, it matches the first line of the scenario. The value of each matched group gets yielded to the block as a string. You must take care to have the same number of regular expression groups and block arguments. Since block arguments are always strings, you have to do any type conversions inside the block, or use Step Argument Transforms. - -When Cucumber prints the results of the running features it will underline all step arguments so that it’s easier to see what part of a step was actually recognised as an argument. It will also print the path and line of the matching step definition. This makes it easy to go from a feature file to any step definition. - -Take a look at [Step Definitions](step-definitions.md) and the examples directory to see more. +In addition to a scenario, a feature may contain a [Background](#background), [Scenario Outline](#background) and [Examples](#background). +Respective keywords (in English) and places to read more about them are listed below. +You can get a list of localized keywords with `cucumber --i18n [LANG]`. + +* [Feature](#feature) +* [Background](#background) +* [Scenario](#feature) +* [Scenario Outline](#background) +* [Examples](#background) +* [Given/When/Then (Steps)](step_definitions.md#steps) ## Background {#background} A background section in a feature file allows you to specify a set of steps that are common to every scenario in the file. Instead of having to repeat those steps over and over for each scenario, you move them up into a Background element. There are a couple of advantages to doing this: -A Background is much like a scenario containing a number of steps. The difference is when it is run. The background is run before each of your scenarios but after any of your Before [Hooks](hooks.md). +A Background is much like a scenario containing a number of steps. The difference is when it is run. The background is run before each of your scenarios but after any of your `Before` [Hooks](hooks.md). Example: @@ -132,18 +93,18 @@ Feature: Change PIN #some feature description comes here Background: - Given I have been issued a new card + Given I was issued a new card And I insert the card, entering the correct PIN And I choose "Change PIN" from the menu - Scenario: Change PIN successfully - When I change the PIN to 9876 - Then the system should remember my PIN is now 9876 + Scenario: Change PIN successfully + When I change the PIN to 9876 + Then the system should remember my PIN is now 9876 - Scenario: Try to change PIN to the same as before - When I try to change the PIN to the original PIN number - Then I should see a warning message - And the system should not have changed my PIN + Scenario: Try to change PIN to the same as before + When I try to change the PIN to the original PIN number + Then I should see a warning message + And the system should not have changed my PIN ``` You can have a single `Background` element per feature file, and it must appear before any of the `Scenario` or `Scenario Outline` elements. Just like all the other _Gherkin_ elements, you can give it a name, and you have space to put a multiline description before the first step. For example: @@ -164,7 +125,7 @@ Feature: Change PIN ... ``` -### Good practices for using Background +### Good practices * Don’t use `Background` to set up complicated state unless that state is actually something the client needs to know. * Keep your `Background` section short. diff --git a/docs/hooks.md b/docs/hooks.md index 05be4979ad7..ebaea147906 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -1,16 +1,16 @@ # Hooks -Cucumber provides a number of hooks which allow us to run blocks at various points in the Cucumber test cycle. You can define them anywhere in your support or step definition layers, using the methods `Before` and `After`. Hooks are global by default, meaning they run for every scenario in your -features. If you want them to run for just certain scenarios, you need to tag -those scenarios and then use a [tagged hook](hooks.md#tagged-hooks). +Cucumber provides a number of hooks which allow us to run blocks at various points in the Cucumber test cycle. You can define them anywhere in your support or step definition layers, using the methods `Before` and `After`. Hooks are global by default, meaning they run for every scenario in your `features`. If you want them to run for just certain scenarios, you need to tag those scenarios and then use a [tagged hook](#tagged-hooks). ## Scenario hooks +### Before hooks + `Before` hooks will be run before the first step of each scenario. They will run in the same order of which they are registered. {% codetabs name="Ruby", type="rb" -%} Before do - # Do something before each scenario. + puts "About to start the scenario" end {%- language name="Java", type="java" -%} {% raw %} @@ -21,16 +21,12 @@ end {% endraw %} {%- endcodetabs %} -or like this: +The main difference between [Background](gherkin.md#background) and `Before` hook is: -{% codetabs name="Ruby", type="rb" -%} -Before do |scenario| - # The +scenario+ argument is optional, but if you use it, you can get the title, - # description, or name (title + description) of the scenario that is about to be - # executed. - Rails.logger.debug "Starting scenario: #{scenario.title}" -end -{%- endcodetabs %} +* a **Background** section allows you to specify a set of steps that are common to **every scenario** in the file +* a **Before** hook will be run **before** the first step of **each scenario** + +### After hooks `After` hooks will be run after the last step of each scenario, even when there are failing, undefined, pending or skipped steps. They will run in the opposite order of which they are registered. {% codetabs name="Ruby", type="rb" -%} @@ -49,36 +45,28 @@ end {% raw %} @After public void afterRunningScenario(Scenario scenario) { - System.out.println("*********** Just finished running scenario: " - + scenario.getStatus()); + if (scenario.isFailed()) { + String subject = scenario.getName(); + sendFailureEmail("Failed scenario" + subject); + } } {% endraw %} {%- endcodetabs %} -`Around` hooks give you more power than Before and After hooks, allowing you to -actually control how many times the scenario is run. An Around hook is passed -two parameters: an object that represents the scenario and a block of code -that, when called, will run the scenario: +or like this: {% codetabs name="Ruby", type="rb" -%} -Around do |scenario, block| - puts "About to run #{scenario.name}" - block.call - puts "Finished running #{scenario.name}" +After('@javascript') do |scenario| if(scenario.failed?) + # taking a screenshot only when there is a browser + page.driver.browser.save_screenshot("html-report/#{scenario.__id__}.png") + embed("#{scenario.__id__}.png", "image/png", "SCREENSHOT") end end {%- endcodetabs %} -`Around` hooks also accept tags, so you can restrict them to run for only some -scenarios, just like `Before` and `After` hooks. One great trick we’ve seen them used for is to run the same scenario twice under different conditions, like this: -{% codetabs name="Ruby", type="rb" -%} -Around('@run_with_and_without_javascript') do |scenario, block| -Capybara.current_driver = Capybara.javascript_driver - block.call - Capybara.use_default_driver - block.call -end -{%- endcodetabs %} +The most common use of `Before` and `After hooks is to clear up any residual state left in external systems like databases or starting browsers or servers so that each scenario starts with the system in a known state. + +It means that the next scenario that comes along starts with a blank slate, and we don’t need to worry about leftovers from the previous scenario. ## Tagged hooks {#tagged-hooks}