Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Behat - Automate your example

Riccardo
December 16, 2014

Behat - Automate your example

Short introduction to Behat 3.x.

Riccardo

December 16, 2014
Tweet

More Decks by Riccardo

Other Decks in Programming

Transcript

  1. Feature: Product basket In order to buy products As a

    customer I need to be able to put interesting products into a basket Development Write feature with Gherkin language
  2. Feature: Product basket In order to buy products As a

    customer I need to be able to put interesting products into a basket Rules: - VAT is 20% - Delivery for basket under £10 is £3 - Delivery for basket over £10 is £2 from discussion Development
  3. Feature: Product basket … Scenario: Buying a single product under

    £10 Given there is a "Sith Lord Lightsaber", which costs £5 When I add the "Sith Lord Lightsaber" to the basket Then I should have 1 product in the basket And the overall basket price should be £9 Scenario: Buying a single product over £10 Given there is a "Sith Lord Lightsaber", which costs £15 When I add the "Sith Lord Lightsaber" to the basket Then I should have 1 product in the basket And the overall basket price should be £20 Development Write scenarios
  4. Feature: Product basket … Scenario: Buying a single product under

    £10 Given there is a "Sith Lord Lightsaber", which costs £5 When I add the "Sith Lord Lightsaber" to the basket Then I should have 1 product in the basket And the overall basket price should be £9 Scenario: Buying a single product over £10 Given there is a "Sith Lord Lightsaber", which costs £15 When I add the "Sith Lord Lightsaber" to the basket Then I should have 1 product in the basket And the overall basket price should be £20 Context Development Action Outcome Write scenarios
  5. class FeatureContext implements Context, SnippetAcceptingContext { public function __construct() {

    } /** * @Given there is a :arg1, which costs £:arg2 */ public function thereIsAWhichCostsPs($arg1, $arg2) { throw new PendingException(); } /** * @When I add the :arg1 to the basket */ public function iAddTheToTheBasket($arg1) { throw new PendingException(); } /** * @Then I should have :arg1 product in the basket */ public function iShouldHaveProductInTheBasket($arg1) { throw new PendingException(); } /** * @Then the overall basket price should be £:arg1 */ public function theOverallBasketPriceShouldBePs($arg1) { throw new PendingException(); } } Development
  6. Feature: Product basket In order to buy products As a

    customer I need to be able to put interesting products into a basket Rules: - VAT is 20% - Delivery for basket under £10 is £3 - Delivery for basket over £10 is £2 Scenario: Buying a single product under £10 #features/basket.feature:11 Given there is a "Sith Lord Lightsaber", which costs £5 When I add the "Sith Lord Lightsaber" to the basket Then I should have 1 product in the basket And the overall basket price should be £9 Scenario: Buying a single product over £10 #features/basket.feature:17 Given there is a "Sith Lord Lightsaber", which costs £15 When I add the "Sith Lord Lightsaber" to the basket Then I should have 1 product in the basket And the overall basket price should be £20 2 scenarios (2 undefined) 8 steps (8 undefined) 0m0.04s (10.18Mb) Development
  7. Development Feature: Product basket In order to buy products As

    a customer I need to be able to put interesting products into a basket Rules: - VAT is 20% - Delivery for basket under £10 is £3 - Delivery for basket over £10 is £2 Scenario: Buying a single product under £10 Given there is a "Sith Lord Lightsaber", which costs £5 When I add the "Sith Lord Lightsaber" to the basket Then I should have 1 product in the basket And the overall basket price should be £9 Scenario: Buying a single product over £10 Given there is a "Sith Lord Lightsaber", which costs £15 When I add the "Sith Lord Lightsaber" to the basket Then I should have 1 product in the basket And the overall basket price should be £20 2 scenarios (2 passed) 8 steps (8 passed) 0m0.08s (11.10Mb)
  8. Gherkin Business Readable, Domain Specific Language created specifically for behavior

    descriptions Gherkin serves as your project’s documentation as well as your project’s automated tests
  9. Gherkin whitespace-oriented language Feature: Some terse yet descriptive text of

    what is desired In order to realize a named business value As an explicit system actor I want to gain some beneficial outcome which furthers the goal Additional text... 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 uses indentation to define structure Line endings terminate statements (called steps)
  10. Gherkin keywords Feature: descriptive text of what is desired Scenario:

    some determinable business situation (aka example) Given, Then, When, And, But: steps used to describes scenario Feature, scenario and steps
  11. Gherkin keywords Tags (organize your features and scenarios) @wip @basket

    Scenario: Buying a single product under £10 Given there is a "Sith Lord Lightsaber", which costs £5 When I add the "Sith Lord Lightsaber" to the basket Then I should have 1 product in the basket And the overall basket price should be £9 > vendor/bin/behat --tags "wip"
  12. Gherkin keywords Scenario Outlines (data provider for steps) Scenario Outline:

    Eating Given there are <start> cucumbers When I eat <eat> cucumbers Then I should have <left> cucumbers Examples: | start | eat | left | | 12 | 5 | 7 | | 20 | 5 | 15 |
  13. Gherkin keywords Backgrounds (dry scenario - removing duplicated steps) running

    before each Scenario but after "BeforeScenario" hooks Background: Steps that run before each scenario Given somethings…
  14. Gherkin keywords Tables (when you want to pass a richer

    data structure from a step to a step definition) Scenario: Given the following people exist: | name | email | phone | | Aslak | [email protected] | 123 | | Joe | [email protected] | 234 | | Bryan | [email protected] | 456 | use Behat\Gherkin\Node\TableNode; // ... /** * @Given the following people exist: */ public function thePeopleExist(TableNode $table) { foreach ($table as $row) { // $row['name'], $row['email'], $row['phone'] } }
  15. Gherkin in many languages # language: it Funzionalità: Internal operations

    In order to stay secret As a secret organization We need to be able to erase past agents' memory Contesto: [Date|Dati|Data|Dato] there is agent A E there is agent B Scenario: Erasing agent memory [Date|Dati|Data|Dato] there is agent J E there is agent K Quando I erase agent K's memory Allora there should be agent J Ma there should not be agent K Schema dello scenario: Erasing other agents' memory [Date|Dati|Data|Dato] there is agent <agent1> E there is agent <agent2> Quando I erase agent <agent2>'s memory Allora there should be agent <agent1> Ma there should not be agent <agent2> Esempi: | agent1 | agent2 | | D | M | > vendor/bin/behat --story-syntax --lang=it
  16. Step definition Turnip expression (human-friendly alternative to Regex) /** *

    @Given there is a :product, which costs £:price */ public function thereIsAWhichCostsPs($product, $price) Given there is a "Sith Lord Lightsaber", which costs £5 Then I should have 1 product in the basket /** * @Then I should have :count product(s) in the basket */ public function iShouldHaveProductInTheBasket($count)
  17. Step definition Regular expression /** * @Given /^there is a

    "([^"]*)", which costs £(\d+)$/ */ public function thereIsAWhichCostsPs($product, $price) Given there is a "Sith Lord Lightsaber", which costs £5 Then I should have 1 product in the basket /** * @Then /^I should have (\d+) product in the basket$/ */ public function iShouldHaveProductInTheBasket($count)
  18. Step argument transformations /** * @Transform :user */ public function

    castUsernameToUser($user) { return new User($user); } /** * @Then a :user, should have :count followers */ public function assertUserHasFollowers(User $user, $count) { … }
  19. Hook BeforeSuite AfterSuite BeforeFeature AfterFeature BeforeScenario AfterScenario BeforeStep AfterStep /**

    * @BeforeSuite */ public static function prepare($scope) { // prepare system for test suite // before it runs }
  20. Hook Tagged hook /** * @BeforeScenario @database,@orm */ public function

    cleanDatabase() { // OR condition // clean database before // @database OR @orm scenarios } /** * @BeforeScenario @database&&@fixtures */ public function cleanDatabaseFixtures() { // AND condition // clean database fixtures // before @database @fixtures
  21. Multiple contexts In Behat 2.x could become very hard to

    maintain all your step definitions and hooks inside a single class
  22. Configuring test suites # behat.yml default: suites: core_features: paths: [

    %paths.base%/features/core ] contexts: [ CoreDomainContext ] api_features: paths: [ %paths.base%/features/api ] contexts: [ ApiContext ] %paths.base% is a special variable in behat.yml that refers to the folder in which behat.yml is stored
  23. Multiple contexts // features/bootstrap/MyAwesomeContext.php use Behat\Behat\Context\Context; class MyAwesomeContext implements Context

    { public function __construct($baseUrl, $tempPath) { $this->baseUrl = $baseUrl; $this->tempPath = $tempPath; } } # behat.yml default: suites: default: contexts: - MyAwesomeContext: - http://localhost:8080 - /var/tmp
  24. Profiles # behat.yml default: suites: core_features: paths: [ %paths.base%/features/core ]

    contexts: [ CoreDomainContext ] ci: formatter: name: junit parameters: output_path: /var/tmp/junit > vendor/bin/behat --profile ci Define different configurations for running your feature suite
  25. Filters # behat.yml default: gherkin: filters: tags: ~@tbc scenarios tagged

    as @tbc will be ignored unless the CLI command is run with a custom filter > vendor/bin/behat --tags=tbc
  26. Filters role filter - you can now have nice actor-based

    suite # behat.yml default: suites: core_features: paths: [ %paths.base%/features/core ] contexts: [ CoreDomainContext ] user_features: paths: [ %paths.base%/features/web ] filters: { role: user } contexts: [ UserContext ] admin_features: paths: [ %paths.base%/features/web ] filters: { role: admin } contexts: [ AdminContext ]