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.

B3b2139e4f2c0eca4efe2379fcebc1c5?s=128

Anna Filina

March 14, 2019
Tweet

Transcript

  1. <animés par la passion> Writing Better Gherkin Scenarios MONTREAL, CANADA

    | MARCH 14, 2019 @afilina
  2. Anna Filina ‣ @ZenikaMontreal ‣ I refactor legacy code. ‣

    I automate tests. ‣ I fix security and performance. ‣ I give talks and workshops.
  3. Some teams spend over 50% of their time on Gherkin

    tests.
  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.
  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.
  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
  7. How This Happens? ‣ Automated data extraction tools. ‣ Executing

    the test, then copying output. ‣ Generic sentences are the death of acceptance tests.
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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!”
  20. What We Did ‣ 74 lines → 10 lines. ‣

    No more tables. ‣ Won’t break with new DB columns. ‣ Actually readable and “approvable".
  21. Remaining problems:
 too many scenarios /
 mixed criteria.

  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.
  23. More scenarios
 != more coverage

  24. Coverage, Visualized

  25. None
  26. None
  27. Happy Path ‣ Given non-existing record. ‣ Given valid record.

    ‣ Then check new record. ‣ Then check import summary (rows_imported).
  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
  29. Alternate Path 1 ‣ Given non-existing record. ‣ Given invalid

    record. ‣ Then check record not saved. ‣ Then check import summary (rows_skipped).
  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
  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!
  32. Alternate Path 2 ‣ Given existing record. ‣ Then check

    record not inserted. ‣ Then check import summary (rows_skipped).
  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
  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
  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
  36. What We Did ‣ Clarified acceptance criteria. ‣ Wrote documentation.

    ‣ Removed redundant scenarios and assertions. ‣ Split scenarios into more manageable chunks.
  37. Following paths on the diagram ensures better coverage with fewer

    scenarios.
  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
  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.
  40. What Not To Do ‣ Trying to have one assertion

    per scenario. ‣ Verifying the "how". ‣ Mocking internal parts of the system.
  41. Mocking Exceptions

  42. <animés par la passion> THANKS! @afilina @afilina