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

Writing Better Gherkin Scenarios - March 2019

Writing Better Gherkin Scenarios - March 2019

Are your feature files gigantic and unreadable? Do they break every time you add a database column or change a completely unrelated piece of code? Do you have too many scenarios? Do you need to breathe through your nose just to summon the courage to open them up... again? This talk will guide you through some real-world Gherkin monstrosities and how we made them short, readable, and fewer, all by increasing their relevance and coverage.

Anna Filina
PRO

March 14, 2019
Tweet

More Decks by Anna Filina

Other Decks in Programming

Transcript


  1. Writing Better
    Gherkin Scenarios
    MONTREAL, CANADA | MARCH 14, 2019
    @afilina

    View Slide

  2. Anna Filina
    ‣ @ZenikaMontreal
    ‣ I refactor legacy code.
    ‣ I automate tests.
    ‣ I fix security and performance.
    ‣ I give talks and workshops.

    View Slide

  3. Some teams spend over 50%
    of their time on Gherkin tests.

    View Slide

  4. What’s Wrong?
    ‣ Feature files are too big.
    ‣ Scenarios are unreadable.
    ‣ Things break on tiny DB/CSV/XML changes.
    ‣ There are too many scenarios.
    ‣ There are mistakes in the scenarios, leading to uncaught bugs.

    View Slide

  5. Example: Acceptance Criteria
    ‣ Load data file.
    ‣ Insert valid records.
    ‣ Skip existing records.
    ‣ For invalid records, log warning and continue with next record.
    • City: Invalid city code “{code}”
    • Negative value: Invalid {column} value “{value}”.
    • Yes/no value (rain_today = maybe).
    ‣ Create import summary: imported vs skipped rows.

    View Slide

  6. Feature: Import Weather
    In order to have weather data
    As TLA 1.2.1.7
    I need to import and validate weather data from a CSV
    Scenario: TLA1217-A01
    Given File tla1217-a01.csv exists in files
    Given Table import_log is empty
    Given Table country contains:
    | id | name | area | population | timezone_offset | timezone_offset_dst | created_date | updated_date |
    | 1 | Germany | 357386 | 83000000 | 1 | 2 | 2019-01-01 | 2019-01-01 |
    | 2 | Lithuania | 357386 | 83000000 | 1 | 2 | 2019-01-01 | 2019-01-01 |
    | 3 | Argentina | 357386 | 83000000 | 1 | 2 | 2019-01-01 | 2019-01-01 |
    Given Table city contains:
    | id | country_id | area | population | elevation | latitude | longitude | geocode | created_date | updated_date | name | code |
    | 1 | 1 | 891.7 | 3748148 | 34 | 52.516667 | 13.388889 | DE3 | 2019-01-01 | 2019-01-01 | Berlin | DE-BER |
    | 2 | 1 | 891.7 | 3748148 | 34 | 52.516667 | 13.388889 | DE3 | 2019-01-01 | 2019-01-01 | Vilnius | LT-VLN |
    | 3 | 1 | 891.7 | 3748148 | 34 | 52.516667 | 13.388889 | DE3 | 2019-01-01 | 2019-01-01 | Buenos Aires | AR-BAI |
    Given Table weather_line contains:
    | id | min_temp | max_temp | rainfall | evaporation | sunshine | wind_gust_dir | wind_gust_speed | wind_dir_9am | wind_dir3_pm | wind_speed9_am | wind_speed3_pm | humidity9_am | humidity3_pm | pressure9_am | pressure3_pm | cloud9_am | cloud3_pm | temp9_am | temp3_pm | rain_today | risk_mm | rain_tomorrow | city_code |
    | 1 | 8 | 24.3 | 0 | 3.4 | 6.3 | NW | 30 | SW | NW | 6 | 20 | 68 | 29 | 1019.7 | 1015 | 7 | 7 | 14.4 | 23.6 | No | 3.6 | Yes | DE-BER |
    | 2 | 14 | 26.9 | 3.6 | 4.4 | 9.7 | ENE | 39 | E | W | 4 | 17 | 80 | 36 | 1012.4 | 1008.4 | 5 | 3 | 17.5 | 25.7 | Yes | 3.6 | Yes | DE-BER |
    | 3 | 13.7 | 23.4 | 3.6 | 5.8 | 3.3 | NW | 85 | N | NNE | 6 | 6 | 82 | 69 | 1009.5 | 1007.2 | 8 | 7 | 15.4 | 20.2 | Yes | 39.8 | Yes | DE-BER |
    | 4 | 13.3 | 15.5 | 39.8 | 7.2 | 9.1 | NW | 54 | WNW | W | 30 | 24 | 62 | 56 | 1005.5 | 1007 | 2 | 7 | 13.5 | 14.1 | Yes | 2.8 | Yes | DE-BER |
    | 5 | 7.6 | 16.1 | 2.8 | 5.6 | 10.6 | SSE | 50 | SSE | ESE | 20 | 28 | 68 | 49 | 1018.3 | 1018.5 | 7 | 7 | 11.1 | 15.4 | Yes | 0 | No | DE-BER |
    | 6 | 6.2 | 16.9 | 0 | 5.8 | 8.2 | SE | 44 | SE | E | 20 | 24 | 70 | 57 | 1023.8 | 1021.7 | 7 | 5 | 10.9 | 14.8 | No | 0.2 | No | DE-BER |
    | 7 | 6.1 | 18.2 | 0.2 | 4.2 | 8.4 | SE | 43 | SE | ESE | 19 | 26 | 63 | 47 | 1024.6 | 1022.2 | 4 | 6 | 12.4 | 17.3 | No | 0 | No | DE-BER |
    | 8 | 8.3 | 17 | 0 | 5.6 | 4.6 | E | 41 | SE | E | 11 | 24 | 65 | 57 | 1026.2 | 1024.2 | 6 | 7 | 12.1 | 15.5 | No | 0 | No | DE-BER |
    | 9 | 8.8 | 19.5 | 0 | 4 | 4.1 | S | 48 | E | ENE | 19 | 17 | 70 | 48 | 1026.1 | 1022.7 | 7 | 7 | 14.1 | 18.9 | No | 16.2 | Yes | DE-BER |
    | 10 | 8.4 | 22.8 | 16.2 | 5.4 | 7.7 | E | 31 | S | ESE | 7 | 6 | 82 | 32 | 1024.1 | 1020.7 | 7 | 1 | 13.3 | 21.7 | Yes | 0 | No | DE-BER |
    | 11 | 9.1 | 25.2 | 0 | 4.2 | 11.9 | N | 30 | SE | NW | 6 | 9 | 74 | 34 | 1024.4 | 1021.1 | 1 | 2 | 14.6 | 24 | No | 0.2 | No | DE-BER |
    | 12 | 8.5 | 27.3 | 0.2 | 7.2 | 12.5 | E | 41 | E | NW | 2 | 15 | 54 | 35 | 1023.8 | 1019.9 | 0 | 3 | 16.8 | 26 | No | 0 | No | DE-BER |
    | 13 | 10.1 | 27.9 | 0 | 7.2 | 13 | WNW | 30 | S | NW | 6 | 7 | 62 | 29 | 1022 | 1017.1 | 0 | 1 | 17 | 27.1 | No | 0 | No | DE-BER |
    | 14 | 12.1 | 30.9 | 0 | 6.2 | 12.4 | NW | 44 | WNW | W | 7 | 20 | 67 | 20 | 1017.3 | 1013.1 | 1 | 4 | 19.7 | 30.7 | No | 0 | No | DE-BER |
    | 15 | 10.1 | 31.2 | 0 | 8.8 | 13.1 | NW | 41 | S | W | 6 | 20 | 45 | 16 | 1018.2 | 1013.7 | 0 | 1 | 18.7 | 30.4 | No | 0 | No | DE-BER |
    When I execute TLA1217
    Then Table import_summary contains:
    | id | created_date | status | rows_imported | rows_skipped |
    | 1 | 2019-01-01 | success | 7 | 5 |
    Then Table weather_line contains:
    | id | min_temp | max_temp | rainfall | evaporation | sunshine | wind_gust_dir | wind_gust_speed | wind_dir_9am | wind_dir3_pm | wind_speed9_am | wind_speed3_pm | humidity9_am | humidity3_pm | pressure9_am | pressure3_pm | cloud9_am | cloud3_pm | temp9_am | temp3_pm | rain_today | risk_mm | rain_tomorrow | city_code |
    | 1 | 8 | 24.3 | 0 | 3.4 | 6.3 | NW | 30 | SW | NW | 6 | 20 | 68 | 29 | 1019.7 | 1015 | 7 | 7 | 14.4 | 23.6 | No | 3.6 | Yes | DE-BER |
    | 2 | 14 | 26.9 | 3.6 | 4.4 | 9.7 | ENE | 39 | E | W | 4 | 17 | 80 | 36 | 1012.4 | 1008.4 | 5 | 3 | 17.5 | 25.7 | Yes | 3.6 | Yes | DE-BER |
    | 3 | 13.7 | 23.4 | 3.6 | 5.8 | 3.3 | NW | 85 | N | NNE | 6 | 6 | 82 | 69 | 1009.5 | 1007.2 | 8 | 7 | 15.4 | 20.2 | Yes | 39.8 | Yes | DE-BER |
    | 4 | 13.3 | 15.5 | 39.8 | 7.2 | 9.1 | NW | 54 | WNW | W | 30 | 24 | 62 | 56 | 1005.5 | 1007 | 2 | 7 | 13.5 | 14.1 | Yes | 2.8 | Yes | DE-BER |
    | 5 | 7.6 | 16.1 | 2.8 | 5.6 | 10.6 | SSE | 50 | SSE | ESE | 20 | 28 | 68 | 49 | 1018.3 | 1018.5 | 7 | 7 | 11.1 | 15.4 | Yes | 0 | No | DE-BER |
    | 6 | 6.2 | 16.9 | 0 | 5.8 | 8.2 | SE | 44 | SE | E | 20 | 24 | 70 | 57 | 1023.8 | 1021.7 | 7 | 5 | 10.9 | 14.8 | No | 0.2 | No | DE-BER |
    | 7 | 6.1 | 18.2 | 0.2 | 4.2 | 8.4 | SE | 43 | SE | ESE | 19 | 26 | 63 | 47 | 1024.6 | 1022.2 | 4 | 6 | 12.4 | 17.3 | No | 0 | No | DE-BER |
    | 8 | 8.3 | 17 | 0 | 5.6 | 4.6 | E | 41 | SE | E | 11 | 24 | 65 | 57 | 1026.2 | 1024.2 | 6 | 7 | 12.1 | 15.5 | No | 0 | No | DE-BER |
    | 9 | 8.8 | 19.5 | 0 | 4 | 4.1 | S | 48 | E | ENE | 19 | 17 | 70 | 48 | 1026.1 | 1022.7 | 7 | 7 | 14.1 | 18.9 | No | 16.2 | Yes | DE-BER |
    | 10 | 8.4 | 22.8 | 16.2 | 5.4 | 7.7 | E | 31 | S | ESE | 7 | 6 | 82 | 32 | 1024.1 | 1020.7 | 7 | 1 | 13.3 | 21.7 | Yes | 0 | No | DE-BER |
    | 11 | 9.1 | 25.2 | 0 | 4.2 | 11.9 | N | 30 | SE | NW | 6 | 9 | 74 | 34 | 1024.4 | 1021.1 | 1 | 2 | 14.6 | 24 | No | 0.2 | No | DE-BER |
    | 12 | 8.5 | 27.3 | 0.2 | 7.2 | 12.5 | E | 41 | E | NW | 2 | 15 | 54 | 35 | 1023.8 | 1019.9 | 0 | 3 | 16.8 | 26 | No | 0 | No | DE-BER |
    | 13 | 10.1 | 27.9 | 0 | 7.2 | 13 | WNW | 30 | S | NW | 6 | 7 | 62 | 29 | 1022 | 1017.1 | 0 | 1 | 17 | 27.1 | No | 0 | No | DE-BER |
    | 14 | 12.1 | 30.9 | 0 | 6.2 | 12.4 | NW | 44 | WNW | W | 7 | 20 | 67 | 20 | 1017.3 | 1013.1 | 1 | 4 | 19.7 | 30.7 | No | 0 | No | DE-BER |
    | 15 | 10.1 | 31.2 | 0 | 8.8 | 13.1 | NW | 41 | S | W | 6 | 20 | 45 | 16 | 1018.2 | 1013.7 | 0 | 1 | 18.7 | 30.4 | No | 0 | No | DE-BER |
    | 16 | 12.4 | 32.1 | 0 | 8.4 | 11.1 | E | 46 | SE | WSW | 7 | 9 | 70 | 22 | 1017.9 | 1012.8 | 0 | 3 | 19.1 | 30.7 | No | 0 | No | DE-BER |
    | 17 | 13.8 | 31.2 | 0 | 7.2 | 8.4 | ESE | 44 | WSW | W | 6 | 19 | 72 | 23 | 1014.4 | 1009.8 | 7 | 6 | 20.2 | 29.8 | No | 1.2 | Yes | DE-BER |
    | 18 | 11.7 | 30 | 1.2 | 7.2 | 10.1 | S | 52 | SW | NE | 6 | 11 | 59 | 26 | 1016.4 | 1013 | 1 | 5 | 20.1 | 28.6 | Yes | 0.6 | No | DE-BER |
    | 19 | 12.4 | 32.3 | 0.6 | 7.4 | 13 | E | 39 | NNE | W | 4 | 17 | 60 | 25 | 1017.1 | 1013.3 | 1 | 3 | 20.2 | 31.2 | No | 0 | No | DE-BER |
    | 20 | 15.6 | 33.4 | 0 | 8 | 10.4 | NE | 33 | NNW | NNW | 2 | 13 | 61 | 27 | 1018.5 | 1013.7 | 0 | 1 | 22.8 | 32 | No | 0 | No | DE-BER |
    | 21 | 15.3 | 33.4 | 0 | 8.8 | 9.5 | WNW | 59 | N | NW | 2 | 31 | 60 | 26 | 1012.4 | 1006.5 | 1 | 5 | 22.2 | 32.8 | No | 0.4 | No | DE-BER |
    | 22 | 16.4 | 19.4 | 0.4 | 9.2 | 0 | E | 26 | ENE | E | 6 | 11 | 88 | 72 | 1010.7 | 1008.9 | 8 | 8 | 16.5 | 18.3 | No | 25.8 | Yes | DE-BER |
    Then Table import_log contains:
    | id | created_date | type | message |
    | 1 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 2 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 3 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 4 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 5 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    Many columns
    Many rows

    View Slide

  7. How This Happens?
    ‣ Automated data extraction tools.
    ‣ Executing the test, then copying output.
    ‣ Generic sentences are the death of acceptance tests.

    View Slide

  8. Given File tla1217-a01.csv exists in files
    Given Table import_log is empty
    Given Table country contains:
    | id | name | area | population | timezone_offset | timezone_offset_dst | c
    | 1 | Germany | 357386 | 83000000 | 1 | 2 | 2
    | 2 | Lithuania | 357386 | 83000000 | 1 | 2 | 2
    | 3 | Argentina | 357386 | 83000000 | 1 | 2 | 2
    Given Table city contains:
    | id | country_id | area | population | elevation | latitude | longitude | geoco
    | 1 | 1 | 891.7 | 3748148 | 34 | 52.516667 | 13.388889 | DE3
    | 2 | 1 | 891.7 | 3748148 | 34 | 52.516667 | 13.388889 | DE3
    | 3 | 1 | 891.7 | 3748148 | 34 | 52.516667 | 13.388889 | DE3
    Given Table weather_line contains:
    | id | min_temp | max_temp | rainfall | evaporation | sunshine | wind_gust_dir | w
    | 1 | 8 | 24.3 | 0 | 3.4 | 6.3 | NW | 3

    View Slide

  9. Given Table weather_line contains:
    | id | min_temp | max_temp | rainfall | evaporation | sunshin
    | 1 | 8 | 24.3 | 0 | 3.4 | 6.3
    | 2 | 14 | 26.9 | 3.6 | 4.4 | 9.7
    | 3 | 13.7 | 23.4 | 3.6 | 5.8 | 3.3
    | 4 | 13.3 | 15.5 | 39.8 | 7.2 | 9.1
    | 5 | 7.6 | 16.1 | 2.8 | 5.6 | 10.6
    Given Weather line exists for DE-BER / 2019-01-01

    View Slide

  10. Given Table city contains:
    | id | country_id | area | population | elevation | latitude
    | 1 | 1 | 891.7 | 3748148 | 34 | 52.51666
    | 2 | 1 | 891.7 | 3748148 | 34 | 52.51666
    | 3 | 1 | 891.7 | 3748148 | 34 | 52.51666
    Given City exists for CA-MTL

    View Slide

  11. Given Table country contains:
    | id | name | area | population | timezone_offset | ti
    | 1 | Germany | 357386 | 83000000 | 1 | 2
    | 2 | Lithuania | 357386 | 83000000 | 1 | 2
    | 3 | Argentina | 357386 | 83000000 | 1 | 2

    View Slide

  12. Given File tla1217-a01.csv exists in files
    Given Table import_log is empty
    Given File contains record for DE-BER / 2019-01-01
    Given File contains record for FR-PAR / 2019-01-01
    Given File contains record for CA-MTL / 2019-01-01

    View Slide

  13. Given City exists for CA-MTL
    And Weather line exists for DE-BER / 2019-01-01
    And File contains record for DE-BER / 2019-01-01
    And File contains record for FR-PAR / 2019-01-01
    And File contains record for CA-MTL / 2019-01-01

    View Slide

  14. Then Table import_log contains:
    | id | created_date | type | message |
    | 1 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 2 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 3 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 4 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 5 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    Then Table import_summary contains:
    | id | created_date | rows_imported | rows_skipped |
    | 1 | 2019-01-01 | 7 | 5 |
    Then Table weather_line contains:
    | id | min_temp | max_temp | rainfall | evaporation | sunshine | wind_gust_dir | w
    | 1 | 8 | 24.3 | 0 | 3.4 | 6.3 | NW | 3
    | 2 | 14 | 26.9 | 3.6 | 4.4 | 9.7 | ENE | 3
    | 3 | 13.7 | 23.4 | 3.6 | 5.8 | 3.3 | NW | 8
    | 4 | 13.3 | 15.5 | 39.8 | 7.2 | 9.1 | NW | 5
    | 5 | 7.6 | 16.1 | 2.8 | 5.6 | 10.6 | SSE | 5

    View Slide

  15. Then Table import_log contains:
    | id | created_date | type | message |
    | 1 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 2 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 3 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 4 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 5 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    Then Warning Invalid city code "FR-PAR" should be logged

    View Slide

  16. Then Table import_summary contains:
    | id | created_date | rows_imported | rows_skipped |
    | 1 | 2019-01-01 | 7 | 5 |
    Then Import summary should contain 1 imported and 2 skipped
    rows

    View Slide

  17. Then Table weather_line contains:
    | id | min_temp | max_temp | rainfall | evaporation | sunshin
    | 1 | 8 | 24.3 | 0 | 3.4 | 6.3
    | 2 | 14 | 26.9 | 3.6 | 4.4 | 9.7
    | 3 | 13.7 | 23.4 | 3.6 | 5.8 | 3.3
    | 4 | 13.3 | 15.5 | 39.8 | 7.2 | 9.1
    | 5 | 7.6 | 16.1 | 2.8 | 5.6 | 10.6
    | 6 | 6.2 | 16.9 | 0 | 5.8 | 8.2
    Then 1 weather line should have been created

    View Slide

  18. Then 1 weather line should have been created
    And Warning Invalid city code "FR-PAR" should be logged
    And Import summary should contain 1 imported and 2 skipped
    rows

    View Slide

  19. Feature: Import Weather
    In order to have weather data
    As TLA 1.2.1.7
    I need to import and validate weather data from a CSV
    Scenario: TLA1217-A01
    Given File tla1217-a01.csv exists in files
    Given Table import_log is empty
    Given Table country contains:
    | id | name | area | population | timezone_offset | timezone_offset_dst | created_date | updated_date |
    | 1 | Germany | 357386 | 83000000 | 1 | 2 | 2019-01-01 | 2019-01-01 |
    | 2 | Lithuania | 357386 | 83000000 | 1 | 2 | 2019-01-01 | 2019-01-01 |
    | 3 | Argentina | 357386 | 83000000 | 1 | 2 | 2019-01-01 | 2019-01-01 |
    Given Table city contains:
    | id | country_id | area | population | elevation | latitude | longitude | geocode | created_date | updated_date | name | code |
    | 1 | 1 | 891.7 | 3748148 | 34 | 52.516667 | 13.388889 | DE3 | 2019-01-01 | 2019-01-01 | Berlin | DE-BER |
    | 2 | 1 | 891.7 | 3748148 | 34 | 52.516667 | 13.388889 | DE3 | 2019-01-01 | 2019-01-01 | Vilnius | LT-VLN |
    | 3 | 1 | 891.7 | 3748148 | 34 | 52.516667 | 13.388889 | DE3 | 2019-01-01 | 2019-01-01 | Buenos Aires | AR-BAI |
    Given Table weather_line contains:
    | id | min_temp | max_temp | rainfall | evaporation | sunshine | wind_gust_dir | wind_gust_speed | wind_dir_9am | wind_dir3_pm | wind_speed9_am | wind_speed3_pm | humidity9_am | humidity3_pm | pressure9_am | pressure3_pm | cloud9_am | cloud3_pm | temp9_am | temp3_pm | rain_today | risk_mm | rain_tomorrow | city_code |
    | 1 | 8 | 24.3 | 0 | 3.4 | 6.3 | NW | 30 | SW | NW | 6 | 20 | 68 | 29 | 1019.7 | 1015 | 7 | 7 | 14.4 | 23.6 | No | 3.6 | Yes | DE-BER |
    | 2 | 14 | 26.9 | 3.6 | 4.4 | 9.7 | ENE | 39 | E | W | 4 | 17 | 80 | 36 | 1012.4 | 1008.4 | 5 | 3 | 17.5 | 25.7 | Yes | 3.6 | Yes | DE-BER |
    | 3 | 13.7 | 23.4 | 3.6 | 5.8 | 3.3 | NW | 85 | N | NNE | 6 | 6 | 82 | 69 | 1009.5 | 1007.2 | 8 | 7 | 15.4 | 20.2 | Yes | 39.8 | Yes | DE-BER |
    | 4 | 13.3 | 15.5 | 39.8 | 7.2 | 9.1 | NW | 54 | WNW | W | 30 | 24 | 62 | 56 | 1005.5 | 1007 | 2 | 7 | 13.5 | 14.1 | Yes | 2.8 | Yes | DE-BER |
    | 5 | 7.6 | 16.1 | 2.8 | 5.6 | 10.6 | SSE | 50 | SSE | ESE | 20 | 28 | 68 | 49 | 1018.3 | 1018.5 | 7 | 7 | 11.1 | 15.4 | Yes | 0 | No | DE-BER |
    | 6 | 6.2 | 16.9 | 0 | 5.8 | 8.2 | SE | 44 | SE | E | 20 | 24 | 70 | 57 | 1023.8 | 1021.7 | 7 | 5 | 10.9 | 14.8 | No | 0.2 | No | DE-BER |
    | 7 | 6.1 | 18.2 | 0.2 | 4.2 | 8.4 | SE | 43 | SE | ESE | 19 | 26 | 63 | 47 | 1024.6 | 1022.2 | 4 | 6 | 12.4 | 17.3 | No | 0 | No | DE-BER |
    | 8 | 8.3 | 17 | 0 | 5.6 | 4.6 | E | 41 | SE | E | 11 | 24 | 65 | 57 | 1026.2 | 1024.2 | 6 | 7 | 12.1 | 15.5 | No | 0 | No | DE-BER |
    | 9 | 8.8 | 19.5 | 0 | 4 | 4.1 | S | 48 | E | ENE | 19 | 17 | 70 | 48 | 1026.1 | 1022.7 | 7 | 7 | 14.1 | 18.9 | No | 16.2 | Yes | DE-BER |
    | 10 | 8.4 | 22.8 | 16.2 | 5.4 | 7.7 | E | 31 | S | ESE | 7 | 6 | 82 | 32 | 1024.1 | 1020.7 | 7 | 1 | 13.3 | 21.7 | Yes | 0 | No | DE-BER |
    | 11 | 9.1 | 25.2 | 0 | 4.2 | 11.9 | N | 30 | SE | NW | 6 | 9 | 74 | 34 | 1024.4 | 1021.1 | 1 | 2 | 14.6 | 24 | No | 0.2 | No | DE-BER |
    | 12 | 8.5 | 27.3 | 0.2 | 7.2 | 12.5 | E | 41 | E | NW | 2 | 15 | 54 | 35 | 1023.8 | 1019.9 | 0 | 3 | 16.8 | 26 | No | 0 | No | DE-BER |
    | 13 | 10.1 | 27.9 | 0 | 7.2 | 13 | WNW | 30 | S | NW | 6 | 7 | 62 | 29 | 1022 | 1017.1 | 0 | 1 | 17 | 27.1 | No | 0 | No | DE-BER |
    | 14 | 12.1 | 30.9 | 0 | 6.2 | 12.4 | NW | 44 | WNW | W | 7 | 20 | 67 | 20 | 1017.3 | 1013.1 | 1 | 4 | 19.7 | 30.7 | No | 0 | No | DE-BER |
    | 15 | 10.1 | 31.2 | 0 | 8.8 | 13.1 | NW | 41 | S | W | 6 | 20 | 45 | 16 | 1018.2 | 1013.7 | 0 | 1 | 18.7 | 30.4 | No | 0 | No | DE-BER |
    When I execute TLA1217
    Then Table import_summary contains:
    | id | created_date | status | rows_imported | rows_skipped |
    | 1 | 2019-01-01 | success | 7 | 5 |
    Then Table weather_line contains:
    | id | min_temp | max_temp | rainfall | evaporation | sunshine | wind_gust_dir | wind_gust_speed | wind_dir_9am | wind_dir3_pm | wind_speed9_am | wind_speed3_pm | humidity9_am | humidity3_pm | pressure9_am | pressure3_pm | cloud9_am | cloud3_pm | temp9_am | temp3_pm | rain_today | risk_mm | rain_tomorrow | city_code |
    | 1 | 8 | 24.3 | 0 | 3.4 | 6.3 | NW | 30 | SW | NW | 6 | 20 | 68 | 29 | 1019.7 | 1015 | 7 | 7 | 14.4 | 23.6 | No | 3.6 | Yes | DE-BER |
    | 2 | 14 | 26.9 | 3.6 | 4.4 | 9.7 | ENE | 39 | E | W | 4 | 17 | 80 | 36 | 1012.4 | 1008.4 | 5 | 3 | 17.5 | 25.7 | Yes | 3.6 | Yes | DE-BER |
    | 3 | 13.7 | 23.4 | 3.6 | 5.8 | 3.3 | NW | 85 | N | NNE | 6 | 6 | 82 | 69 | 1009.5 | 1007.2 | 8 | 7 | 15.4 | 20.2 | Yes | 39.8 | Yes | DE-BER |
    | 4 | 13.3 | 15.5 | 39.8 | 7.2 | 9.1 | NW | 54 | WNW | W | 30 | 24 | 62 | 56 | 1005.5 | 1007 | 2 | 7 | 13.5 | 14.1 | Yes | 2.8 | Yes | DE-BER |
    | 5 | 7.6 | 16.1 | 2.8 | 5.6 | 10.6 | SSE | 50 | SSE | ESE | 20 | 28 | 68 | 49 | 1018.3 | 1018.5 | 7 | 7 | 11.1 | 15.4 | Yes | 0 | No | DE-BER |
    | 6 | 6.2 | 16.9 | 0 | 5.8 | 8.2 | SE | 44 | SE | E | 20 | 24 | 70 | 57 | 1023.8 | 1021.7 | 7 | 5 | 10.9 | 14.8 | No | 0.2 | No | DE-BER |
    | 7 | 6.1 | 18.2 | 0.2 | 4.2 | 8.4 | SE | 43 | SE | ESE | 19 | 26 | 63 | 47 | 1024.6 | 1022.2 | 4 | 6 | 12.4 | 17.3 | No | 0 | No | DE-BER |
    | 8 | 8.3 | 17 | 0 | 5.6 | 4.6 | E | 41 | SE | E | 11 | 24 | 65 | 57 | 1026.2 | 1024.2 | 6 | 7 | 12.1 | 15.5 | No | 0 | No | DE-BER |
    | 9 | 8.8 | 19.5 | 0 | 4 | 4.1 | S | 48 | E | ENE | 19 | 17 | 70 | 48 | 1026.1 | 1022.7 | 7 | 7 | 14.1 | 18.9 | No | 16.2 | Yes | DE-BER |
    | 10 | 8.4 | 22.8 | 16.2 | 5.4 | 7.7 | E | 31 | S | ESE | 7 | 6 | 82 | 32 | 1024.1 | 1020.7 | 7 | 1 | 13.3 | 21.7 | Yes | 0 | No | DE-BER |
    | 11 | 9.1 | 25.2 | 0 | 4.2 | 11.9 | N | 30 | SE | NW | 6 | 9 | 74 | 34 | 1024.4 | 1021.1 | 1 | 2 | 14.6 | 24 | No | 0.2 | No | DE-BER |
    | 12 | 8.5 | 27.3 | 0.2 | 7.2 | 12.5 | E | 41 | E | NW | 2 | 15 | 54 | 35 | 1023.8 | 1019.9 | 0 | 3 | 16.8 | 26 | No | 0 | No | DE-BER |
    | 13 | 10.1 | 27.9 | 0 | 7.2 | 13 | WNW | 30 | S | NW | 6 | 7 | 62 | 29 | 1022 | 1017.1 | 0 | 1 | 17 | 27.1 | No | 0 | No | DE-BER |
    | 14 | 12.1 | 30.9 | 0 | 6.2 | 12.4 | NW | 44 | WNW | W | 7 | 20 | 67 | 20 | 1017.3 | 1013.1 | 1 | 4 | 19.7 | 30.7 | No | 0 | No | DE-BER |
    | 15 | 10.1 | 31.2 | 0 | 8.8 | 13.1 | NW | 41 | S | W | 6 | 20 | 45 | 16 | 1018.2 | 1013.7 | 0 | 1 | 18.7 | 30.4 | No | 0 | No | DE-BER |
    | 16 | 12.4 | 32.1 | 0 | 8.4 | 11.1 | E | 46 | SE | WSW | 7 | 9 | 70 | 22 | 1017.9 | 1012.8 | 0 | 3 | 19.1 | 30.7 | No | 0 | No | DE-BER |
    | 17 | 13.8 | 31.2 | 0 | 7.2 | 8.4 | ESE | 44 | WSW | W | 6 | 19 | 72 | 23 | 1014.4 | 1009.8 | 7 | 6 | 20.2 | 29.8 | No | 1.2 | Yes | DE-BER |
    | 18 | 11.7 | 30 | 1.2 | 7.2 | 10.1 | S | 52 | SW | NE | 6 | 11 | 59 | 26 | 1016.4 | 1013 | 1 | 5 | 20.1 | 28.6 | Yes | 0.6 | No | DE-BER |
    | 19 | 12.4 | 32.3 | 0.6 | 7.4 | 13 | E | 39 | NNE | W | 4 | 17 | 60 | 25 | 1017.1 | 1013.3 | 1 | 3 | 20.2 | 31.2 | No | 0 | No | DE-BER |
    | 20 | 15.6 | 33.4 | 0 | 8 | 10.4 | NE | 33 | NNW | NNW | 2 | 13 | 61 | 27 | 1018.5 | 1013.7 | 0 | 1 | 22.8 | 32 | No | 0 | No | DE-BER |
    | 21 | 15.3 | 33.4 | 0 | 8.8 | 9.5 | WNW | 59 | N | NW | 2 | 31 | 60 | 26 | 1012.4 | 1006.5 | 1 | 5 | 22.2 | 32.8 | No | 0.4 | No | DE-BER |
    | 22 | 16.4 | 19.4 | 0.4 | 9.2 | 0 | E | 26 | ENE | E | 6 | 11 | 88 | 72 | 1010.7 | 1008.9 | 8 | 8 | 16.5 | 18.3 | No | 25.8 | Yes | DE-BER |
    Then Table import_log contains:
    | id | created_date | type | message |
    | 1 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 2 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 3 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 4 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    | 5 | 2019-01-01 | warning | Invalid city code "FR-PAR" |
    “Yep! This is exactly
    what I said in the
    acceptance criteria!”

    View Slide

  20. What We Did
    ‣ 74 lines → 10 lines.
    ‣ No more tables.
    ‣ Won’t break with new DB columns.
    ‣ Actually readable and “approvable".

    View Slide

  21. Remaining problems:

    too many scenarios /

    mixed criteria.

    View Slide

  22. Many Scenarios
    ‣ Invalid city + existing record.
    ‣ Invalid numeric value (rainfall = -10).
    ‣ Invalid numeric value x 3.
    ‣ Invalid yes/no value (rain_today = maybe).
    ‣ Invalid yes/no value x 3.
    ‣ Combination of city and numeric warnings.
    ‣ Combination of city and yes/no warnings.
    ‣ Combination of numeric and yes/no warnings.
    ‣ Combination of all 3.
    ‣ Combination of all 3... x 3.

    View Slide

  23. More scenarios

    != more coverage

    View Slide

  24. Coverage, Visualized

    View Slide

  25. View Slide

  26. View Slide

  27. Happy Path
    ‣ Given non-existing record.
    ‣ Given valid record.
    ‣ Then check new record.
    ‣ Then check import summary (rows_imported).

    View Slide

  28. Scenario: New and valid record is saved
    Given City exists for DE-BER
    Given File contains valid record for DE-BER / 2019-01-01
    When I execute TLA1217
    Then 1 weather line should have been created
    And Import summary should contain 1 imported and 0 skipped rows

    View Slide

  29. Alternate Path 1
    ‣ Given non-existing record.
    ‣ Given invalid record.
    ‣ Then check record not saved.
    ‣ Then check import summary (rows_skipped).

    View Slide

  30. Scenario: Invalid records are logged and skipped
    Given File contains record for DE-BER / 2019-01-01 with rainfall = -10
    When I execute TLA1217
    Then 1 warning should be logged
    And Import summary should contain 0 imported and 1 skipped rows

    View Slide

  31. Then Warning Invalid city code "CA-MTL" should be logged
    Then Warning Invalid rain_today "maybe" should be logged
    Don't go too
    granular here!

    View Slide

  32. Alternate Path 2
    ‣ Given existing record.
    ‣ Then check record not inserted.
    ‣ Then check import summary (rows_skipped).

    View Slide

  33. Scenario: Existing records are skipped
    Given Weather line exists for DE-BER / 2019-01-01
    And File contains valid record for DE-BER / 2019-01-01
    When I execute TLA1217
    Then 0 weather line should have been created
    And Import summary should contain 0 imported and 1 skipped rows

    View Slide

  34. Acceptance Criteria
    ‣ Load data file.
    ‣ Insert valid records.
    ‣ Skip existing records (city + date).
    ‣ For invalid records, log warning and continue with next record.
    • City: Invalid city code “{code}”
    • Negative value: Invalid {column} “{value}”.
    • Yes/no value (rain_today = maybe).
    ‣ Create import summary: imported vs skipped rows.
    }Moved to

    unit tests

    View Slide

  35. Scenario: Invalid records are logged and skipped, subsequent records are saved
    Given File contains record for DE-BER / 2019-01-01 with rainfall = -10

    And City exists for CA-MTL
    And File contains valid record for CA-MTL / 2019-01-01
    When I execute TLA1217
    Then 1 warning should be logged
    And Import summary should contain 1 imported and 1 skipped rows

    View Slide

  36. What We Did
    ‣ Clarified acceptance criteria.
    ‣ Wrote documentation.
    ‣ Removed redundant scenarios and assertions.
    ‣ Split scenarios into more manageable chunks.

    View Slide

  37. Following paths on the
    diagram ensures better
    coverage with fewer
    scenarios.

    View Slide

  38. Pushing It Further
    ‣ DoR: have a happy path draft.
    • Given non-existing and valid record.
    • When I execute import process.
    • Then record is saved
    • And import summary has 1 imported and 0 skipped rows

    View Slide

  39. Advantages
    ‣ Ensures that everyone understands things the same way.
    ‣ Agree on how to verify the acceptance criteria.
    ‣ Entering a sprint: can start writing tests and code right away.
    ‣ Ask relevant questions early on.

    View Slide

  40. What Not To Do
    ‣ Trying to have one assertion per scenario.
    ‣ Verifying the "how".
    ‣ Mocking internal parts of the system.

    View Slide

  41. Mocking Exceptions

    View Slide


  42. THANKS!
    @afilina
    @afilina

    View Slide