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

Playing Tickle Pickle or Writing BDD tests with Spek 2

Playing Tickle Pickle or Writing BDD tests with Spek 2

We all love to develop cool things using new technologies, but how do we prove the quality of our code? Tests, of course! This is exactly what we usually lack time for and what we hate so much because of the ugliness of ancient JUnit. I’ll tell you how to enjoy writing tests using the right tools.

It’s a talk about our migration from JUnit to Spek, and then to Spek 2, and how this tool helps us write readable specifications and keep them maintainable.

Video: https://onedrive.live.com/?authkey=%21AO4yhXYbMxMob9o&cid=B010081BC037E0BD&id=B010081BC037E0BD%21396907&parId=B010081BC037E0BD%21396884&o=OneUp

Dmytro Zaitsev

June 01, 2019
Tweet

More Decks by Dmytro Zaitsev

Other Decks in Programming

Transcript

  1. Hello! I am Dmytro Zaitsev I am here because I

    love to give presentations. Also, sometimes I develop mobile apps for money. You can find me at @DmitriyZaitsev 2
  2. Agenda ✗ What is Gherkin ✗ Why Gherkin ✗ Gherkin

    syntax ✗ Examples ✗ Best practices ✗ Pros & Cons 3
  3. Structure Tests are written using nested lambdas, each scope (level)

    can either be a group or a test. ✗ Test scope is where you place your assertions/ checks (in JUnit dialect - this is your test method). ✗ Group scope is used to organize your tests. ✗ It can contain test scopes and other group scopes as well. ✗ Group scopes will be eagerly executed during the discovery phase. 12
  4. 13 object MySpek: Spek({ group("a group") { test("a test") {

    ... } group("a nested group") { test("another test") { ... } } } })
  5. Discovery The goal of this phase is to build the

    test tree which outlines how the tests are executed. In order to achieve this, Spek will execute all group scopes starting from the root. Phases Execution In this phase the tests are executed. Spek traverses the test tree starting from the root and for each scope it will execute the registered fixtures. 15
  6. 16 object MySpek: Spek({ println("1") group("some group") { println("2") test("some

    test") { println("4") } } group("another group") { println("3") test("another test") { println("5") } } }) Discovery Execution
  7. 19 object MySpek: Spek({ beforeGroup { println("before root") } group("some

    group") { beforeEachTest { println("before each test") } test("some test") { println("some test") } afterEachTest { println("after each test") } } afterGroup { println("after root”) } })
  8. 20 object MySpek: Spek({ beforeGroup { println("before root") } group("some

    group") { beforeEachTest { println("before each test") } test("some test") { println("some test") } afterEachTest { println("after each test") } } afterGroup { println("after root”) } })
  9. Caching modes You can pass in an optional parameter to

    memoized which controls how the values are cached. ✗ CachingMode.TEST: each test scope will receive a unique instance, this is the default. ✗ CachingMode.GROUP: each group scope will receive a unique instance. ✗ CachingMode.SCOPE: effectively a singleton. ✗ CachingMode.INHERIT: internal use only. 24
  10. ✗ Gherkin is a Business Readable, DSL created especially for

    behavior descriptions. ✗ It gives you the ability to remove logic details from behavior tests. 26
  11. Gherkin syntax 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 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 ... 29
  12. Feature ✗ A feature usually contains a list of scenarios.

    ✗ You can use group features and scenarios together. ✗ Every scenario consists of a list of steps, which must start with one of the keywords Given, When, Then, or And (or localized one). Spek treats them all the same, but you shouldn’t. 30
  13. Scenario ✗ Scenario is one of the core Gherkin structures.

    ✗ Every scenario starts with the Scenario: keyword, followed by an optional scenario title. ✗ Each feature can have one or more scenarios ✗ Every scenario consists of one or more steps. 31
  14. Example 32 Feature: Serve coffee In order to earn money

    Customers should be able to buy coffee at all times Scenario: Buy coffee Given there are 1 coffees left in the machine And I have deposited 1 dollar When I press the coffee button Then I should be served a coffee
  15. 33 Spek Feature 1 Feature 2 Given
 When Then Scenario

    1 Scenario 2 Scenario 1 Given
 When Then Given
 When Then Given
 When Then Given
 When Then
  16. Given ✗ The use of Given keyword is to put

    the system in a familiar state before the user starts interacting with the system. ✗ However, you can omit writing user interactions in Given steps if Given in the "Precondition" step. 35
  17. Then ✗ The use of 'then' keyword is to see

    the outcome after the action in when step. ✗ However, you can only verify noticeable changes. 37
  18. Be aware ✗ Given, When, Then, And are test steps.

    ✗ You can use them interchangeably. ✗ Spek doesn't display any error. However, they will surely not make any 'sense' when read. 39
  19. Scenario outlines Scenario: Eat 5 out of 12 Given there

    are 12 cucumbers When I eat 5 cucumbers Then I should have 7 cucumbers 40
  20. 41 Scenario("Eat 5 out of 12") { lateinit var cucumbers:

    MutableList<Cucumber> Given("There are 12 cucumbers") { cucumbers = MutableList(12) { Cucumber() } } When("I eat 5 cucumbers") { for (i in 0 until 5) cucumbers.removeAt(cucumbers.lastIndex) } Then("I should have 7 cucumbers") { assertEquals(7, cucumbers.size) } }
  21. 42 Scenario("Eat 5 out of 20") { lateinit var cucumbers:

    MutableList<Cucumber> Given("There are 20 cucumbers") { cucumbers = MutableList(20) { Cucumber() } } When("I eat 5 cucumbers") { for (i in 0 until 5) cucumbers.removeAt(cucumbers.lastIndex) } Then("I should have 15 cucumbers") { assertEquals(15, cucumbers.size) } }
  22. Scenario outlines 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 | 43
  23. 44 listOf( Example(start = 12, eat = 5), Example(start =

    20, eat = 5) ).forEach { o -> Scenario("Eat ${o.eat} out of ${o.start}") { lateinit var cucumbers: MutableList<Cucumber> Given("There are ${o.start} cucumbers") { cucumbers = MutableList(o.start) { Cucumber() } } When("I eat ${o.eat} cucumbers") { for (i in 0 until o.eat) cucumbers.removeAt(cucumbers.lastIndex) } Then("I should have ${o.left} cucumbers") { assertEquals(o.left, cucumbers.size) } } }
  24. Best practices ✗ Each scenario should execute separately ✗ Every

    feature should able to be executed along ✗ Steps information should be shown independently ✗ Connect your Scenario's with your requirements ✗ Keep a complete track of what scenarios should be included in a requirement document ✗ Create modular and easy to understand steps ✗ Try to combine all your common scenarios 46
  25. Pros ✗ Gherkin is simple enough for non-programmers to understand

    ✗ Programmers can use it as a very solid base to start their tests ✗ It makes User Stories easier to digest ✗ Gherkin script can easily understand by business executives and developers ✗ Targets the business requirements ✗ A significant proportion of the functional specifications is written as user stories ✗ You don't need to be expert to understand the small Gherkin command set ✗ Gherkin links acceptance tests directly to automated tests ✗ Style of writing tests cases are easier to reuse code in other tests 48
  26. Cons ✗ It requires a high level of business engagement

    and collaborations ✗ May not work well in all scenarios ✗ Poorly written tests can easily increase test- maintenance cost 49
  27. 51