Slide 1

Slide 1 text

Playing Tickle Pickle or Writing BDD tests with Spek 2

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

Agenda ✗ What is Gherkin ✗ Why Gherkin ✗ Gherkin syntax ✗ Examples ✗ Best practices ✗ Pros & Cons 3

Slide 4

Slide 4 text

Our code evolution Java Kotlin 4

Slide 5

Slide 5 text

Our tests evolution JUnit Spek Spek2 5

Slide 6

Slide 6 text

“Tests are specifications” 6

Slide 7

Slide 7 text

“Test after is not BDD” 7

Slide 8

Slide 8 text

“Tests do not drive the implementation when they are written afterwards.” 8

Slide 9

Slide 9 text

“BDD is not about testing. It’s about development” 9

Slide 10

Slide 10 text

Spek 2 core concepts

Slide 11

Slide 11 text

Spek 2 Structure

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

13 object MySpek: Spek({ group("a group") { test("a test") { ... } group("a nested group") { test("another test") { ... } } } })

Slide 14

Slide 14 text

Spek 2 Phases

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

17 Root Group 1 Group 2 Test 1 Test 2 Test 1 Test 2

Slide 18

Slide 18 text

Spek 2 Fixtures

Slide 19

Slide 19 text

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”) } })

Slide 20

Slide 20 text

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”) } })

Slide 21

Slide 21 text

Spek 2 Scope values

Slide 22

Slide 22 text

22 object MySpek: Spek({ lateinit var cucumber: Cucumber beforeEachTest { cucumber = Cucumber() } })

Slide 23

Slide 23 text

23 object MySpek: Spek({ val cucumber by memoized { Cucumber() } })

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Writing features Gherkin language

Slide 26

Slide 26 text

✗ Gherkin is a Business Readable, DSL created especially for behavior descriptions. ✗ It gives you the ability to remove logic details from behavior tests. 26

Slide 27

Slide 27 text

✗ project’s documentation ✗ automated tests. 27 Gherkin serves two purposes

Slide 28

Slide 28 text

Gherkin syntax

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Steps

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

When ✗ When is the step to define action performed by user 36

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

And ✗ You may have multiple ‘given’, ‘when’ or ‘then’ 38

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

41 Scenario("Eat 5 out of 12") { lateinit var cucumbers: MutableList 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) } }

Slide 42

Slide 42 text

42 Scenario("Eat 5 out of 20") { lateinit var cucumbers: MutableList 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) } }

Slide 43

Slide 43 text

Scenario outlines Scenario Outline: Eating Given there are cucumbers When I eat cucumbers Then I should have cucumbers Examples: | start | eat | left | | 12 | 5 | 7 | | 20 | 5 | 15 | 43

Slide 44

Slide 44 text

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 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) } } }

Slide 45

Slide 45 text

Best practices

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Pros & Cons

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

Demo

Slide 51

Slide 51 text

51

Slide 52

Slide 52 text

52 Thanks! Any questions? You can find me at: ✗ @DmitriyZaitsev