Testing with Kotlin

26
TESTING WITH KOTLIN Rob Fletcher — @rfletcherEW Senior Enterprise Bean Deployer, Netflix

Transcript of Testing with Kotlin

Page 1: Testing with Kotlin

TESTING WITH KOTLINRob Fletcher — @rfletcherEW

Senior Enterprise Bean Deployer, Netflix

Page 2: Testing with Kotlin

ITERATIVE TESTSone test, many data points

Page 3: Testing with Kotlin

ITERATION – SPOCK

def "the diamond function rejects illegal characters"() { when: diamond.of(c) then: thrown IllegalArgumentException where: c << generator.chars() .findAll { !('A'..'Z').contains(it) } .take(100) }

Page 4: Testing with Kotlin

ITERATION – SPEK

describe("handling invalid characters") {

chars().filter { it !in 'A'..'Z' } .take(100) .forEach { c -> it("throws an exception") assertFailsWith(IllegalArgumentException::class) { diamond.of(c) } } } }

Page 5: Testing with Kotlin

TOKENIZED TEST NAMES – SPOCK

@Unroll def "the diamond function rejects #c"() { when: diamond.of(c)

then: thrown IllegalArgumentException

where: c << generator.chars() .findAll { !('A'..'Z').contains(it) } .take(100) }

Page 6: Testing with Kotlin

TOKENIZED TEST NAMES – SPEK

describe("handling invalid characters") { chars().filter { it !in 'A'..'Z' } .take(100) .forEach { c -> it("throws an exception for '$c'") assertFailsWith(IllegalArgumentException::class) { diamond.of(c) } } } }

Page 7: Testing with Kotlin

NESTED ITERATION – SPEK

('B'..'Z').forEach { c -> describe("the diamond of '$c'") { val result = diamond.of(c)

it("is square") { assert(result.all { it.length == rows.size }) } ('A'..c).forEach { rowChar -> val row = rowChar - 'A' val col = c - rowChar it("has a '$rowChar' at row $row column $col") { assertEquals(rowChar, result[row][col]) } } } }

Page 8: Testing with Kotlin

TABULAR ITERATION – SPOCK

@Unroll def "the diamond of #c has #height rows"() { expect: diamond.of(c).size == height

where: c | height 'A' | 1 'B' | 3 'C' | 5 }

Page 9: Testing with Kotlin

TABULAR ITERATION – SPEK

for ((c, height) in mapOf('A' to 1, 'B' to 2, 'C' to 3)) { describe("the diamond of '$c") { val result = diamond(c) it("has $height rows") { assertEquals(height, result.size) } } }

Page 10: Testing with Kotlin

MOCKSMockito meets Kotlin

Page 11: Testing with Kotlin

MOCKITO – SPEK

describe("publishing events") { val eventBus = EventBus() val subscriber = mock(Subscriber::class.java) as Subscriber<ExampleEvent> beforeEach { eventBus.post(ExampleEvent("test running")) } it("should send the event to the subscriber") { verify(subscriber) .onEvent(argThat { (it as ExampleEvent).description == "test running" }) } }

Page 12: Testing with Kotlin

MOCKITO – SPEK

describe("publishing events") { val eventBus = EventBus() val subscriber = mock(Subscriber::class.java) as Subscriber<ExampleEvent> beforeEach { eventBus.post(ExampleEvent("test running")) } it("should send the event to the subscriber") { verify(subscriber) .onEvent(argThat { (it as ExampleEvent).description == "test running" }) } }

Can't infer generic type

Can't infer generic matcher argument

Page 13: Testing with Kotlin

MOCKITO – SPEK

describe("publishing events") { val eventBus = EventBus() val subscriber = mock(Subscriber::class.java) as Subscriber<ExampleEvent> beforeEach { eventBus.post(ExampleEvent("test running")) } it("should send the event to the subscriber") { verify(subscriber) .onEvent(argThat { (it as ExampleEvent).description == "test running" }) } }

Returns null so fails at runtime

Page 14: Testing with Kotlin

MOCKITO – SPEK

repositories { jcenter() } dependencies { compile "com.nhaarman:mockito-kotlin:0.4.1" }

Page 15: Testing with Kotlin

MOCKITO – SPEK

describe("publishing events") { val eventBus = EventBus() val subscriber: Subscriber<Event> = mock() beforeEach { eventBus.post(Event("test running")) } it("should send the event to the subscriber") { verify(subscriber).onEvent(argThat { description == "test running" }) } }

Page 16: Testing with Kotlin

MOCKITO – SPEK

describe("publishing events") { val eventBus = EventBus() val subscriber: Subscriber<Event> = mock() beforeEach { eventBus.post(Event("test running")) } it("should send the event to the subscriber") { verify(subscriber).onEvent(argThat { description == "test running" }) } }

Type inference on receiver

Reified generics allow type inference

Page 17: Testing with Kotlin

MOCKITO – SPEK

describe("publishing events") { val eventBus = EventBus() val subscriber: Subscriber<Event> = mock() beforeEach { eventBus.post(Event("test running")) } it("should send the event to the subscriber") { verify(subscriber).onEvent(argThat { description == "test running" }) } }

Returns dummy value

Page 18: Testing with Kotlin

TCKSmultiple implementations, one set of tests

Page 19: Testing with Kotlin

TCK – SPOCK

abstract class PubSubSpec extends Specification { @Subject abstract EventBus factory() // ... tests appear here} class SyncPubSubSpec extends PubSubSpec { @Override EventBus factory() { new EventBus() } } class AsyncPubSubSpec extends PubSubSpec { @Override EventBus factory() { new AsyncEventBus(newSingleThreadExecutor()) } }

Page 20: Testing with Kotlin

TCK – SPEK

abstract class PubSubTck(val eventBus: EventBus) : Spek ( { describe("publishing events") { // ... tests appear here } } ) class SyncPubSubSpec : PubSubTck(EventBus()) class AsyncPubSubSpec : PubSubTck(AsyncEventBus(newSingleThreadExecutor()))

Page 21: Testing with Kotlin

TCK WITH FACTORY – SPEK

abstract class PubSubTck(val factory: () -> EventBus) : Spek ( { val eventBus = factory() describe("publishing events") { // ... tests appear here } } ) class SyncPubSubFactorySpec : PubSubTck(::EventBus) class AsyncPubSubFactorySpec : PubSubTck({ AsyncEventBus(newSingleThreadExecutor()) })

Page 22: Testing with Kotlin

DIAGRAMMED ASSERTIONS#1 on my Spek wishlist!

Page 23: Testing with Kotlin

"POWER ASSERT" – SPOCK

expect: order.user.firstName == "Rob"

Page 24: Testing with Kotlin

"POWER ASSERT" – SPOCK

order.user.firstName == "Rob" | | | | | | rob false | | 1 difference (66% similarity) | | (r)ob | | (R)ob | [firstName:rob, lastName:fletcher] Order<#3243627>

Page 25: Testing with Kotlin

DIAGRAMMED ASSERTIONS – SCALATEST

assert(order.getUser.getFirstName == "Rob") | | | | | | rob false | User<firstName:rob, lastName:fletcher> Order<#3243627>

Page 26: Testing with Kotlin

BAY AREA KOTLIN USER GROUP

May 26 / 6pmNetflix in Los Gatos

➤ Netflix Engineering Tools — Kotlin Spring Cloud Extensions

meetup.com/Bay-Area-Kotlin-User-Group