Kotlin all your tests (Mobius 2017)

Kotlin all your tests (Mobius 2017)

Kotlin was the rising star of programming languages in 2016. It not only became final with version 1.0 but also got rapidly adopted in the android community. But many companies are struggling to add Kotlin to an existing Java project. It's not easy to add a new language to an existing team. But it's much easier to introduce Kotlin to your project using the backdoor: your unit tests. So let's look at Kotlin more from a testing perspective. How does it look like to write tests in Kotlin, what possibilities of the language will help us?

A8b79d304b5184e5a5b0a109590f6683?s=128

Danny Preussler

April 21, 2017
Tweet

Transcript

  1. fun test() Kotlin all your tests Danny Preussler Android Lead,

    Google Developer Expert
  2. @PreusslerBerlin

  3. @PreusslerBerlin Our world so far …

  4. @PreusslerBerlin Android Firewall by Uncalno Tekno, CC, flickr.com/photos/uncalno/8538679708

  5. @PreusslerBerlin Watch the cool kids …

  6. @PreusslerBerlin onika Durickova, CC, flickr.com/photos/98873911@N07/15683534672

  7. @PreusslerBerlin But then …

  8. @PreusslerBerlin http://davidojedalopez.com/2016/06/07/getting-started-with-kotlin-for-android-development/

  9. @PreusslerBerlin Fly into new horizons ! (c) Sunny M5, CC

    flickr.com/photos/84935187@N04/16171765851
  10. @PreusslerBerlin Kotlin stole the best from the best

  11. @PreusslerBerlin Kotlin stole got inspired by the best from of

    the best
  12. @PreusslerBerlin Excited by eltpics, CC flickr.com/photos/eltpics/14554630040

  13. @PreusslerBerlin BUT

  14. @PreusslerBerlin The Boss by Joselito TagaraoFollow, CC, flickr.com/photos/lidocaineus/7803665222/

  15. @PreusslerBerlin “No Kotlin in production”

  16. @PreusslerBerlin Depressed by wheatfieldbrown, CC flickr.com/photos/wheatfieldbrown/2233487982

  17. @PreusslerBerlin But wait …

  18. @PreusslerBerlin But wait … A backdoor

  19. @PreusslerBerlin No one cares about your tests anyway right?

  20. @PreusslerBerlin Tests are a safe place for experiments

  21. @PreusslerBerlin Let’s test in Kotlin

  22. @PreusslerBerlin Kotlin is expressive

  23. @PreusslerBerlin @Test void testHandlesNullArguments()

  24. @PreusslerBerlin @Test fun testHandlesNullArguments()

  25. @PreusslerBerlin @Test fun testHandlesNullArguments()

  26. @PreusslerBerlin @Test fun handlesNullArguments()

  27. @PreusslerBerlin @Test fun handles_null_arguments()

  28. @PreusslerBerlin @Test @DisplayName("handles null arguments") fun handlesNullArguments() * Needs JUnit5

  29. @PreusslerBerlin @Test fun ‘handles null arguments’()

  30. @PreusslerBerlin Readability first

  31. @PreusslerBerlin String jsonText = "{\r\n \"deal\" : {\r\n \"isTipped\" :

    true,\r\n \"id\" : \"D- Company-241907\",\r\n \"areas\" : [],\r\n \"smallImageUrl\" : \"http://uat- static.groupon.de/00/00/small0000.jpg\",\r\n \"finePrint\" : \"MULTI DEAL ext Codes\",\r\n \"says\" : {\r\n \"emailContent\" : \"\",\r\n \"id\" : \"\",\r\n \"websiteContent\" : \"\",\r\n \"title\" : \"\",\r\n \"websiteContentHtml\" : \"\",\r\n \"emailContentHtml\" : \"\"\r\n },\r\n \"endAt\" : \"2014-03-31T22:59:00Z\",\r\n \"shortAnnouncementTitle\" : \"D-Company: 50% sparen in QA_AutoTestCity2\",\r\n \"placeholderUrl\" : \"http://assets2.grouponcdn.com/images/groupon/grayPlaceholder.png\",\r\n \"shippingAddressRequired\" : false,\r\n \"type\" : \"groupon\",\r\n \"isSoldOut\" : false,\r\n \"priority\" : 0,\r\n \"options\" : [\r\n {\r\n \"status\" : \"open\",\r\n \"title\" : \"MULTI DEAL ext Codes Option1\",\r\n \"isLimitedQuantity\" : false,\r\n \"tax\" : {\r\n \"amount\" : 16,\r\n \"currencyCode\" : \"EUR\",\r\n \"formattedAmount\" : \"0,16 \u20AC\"\r\n },\r\n \"minimumPurchaseQuantity\" : 1,\r\n \"expiresInDays\" : 62,\r\n \"discount\" : {\r\n \"amount\" : 100,\r\n \"currencyCode\" : \"EUR\",\r\n
  32. @PreusslerBerlin val jsonText = """ { "channels":{ "BOOKING":1443439538, "GTG":1443439537, }

    } """
  33. @PreusslerBerlin Keep it short

  34. @PreusslerBerlin ... Map<TYPE, Integer> signalStrength = new HashMap<>(); signalStrength.put(EDGE, 80);

    signalStrength.put(WIFI to 90); ...
  35. @PreusslerBerlin ... val signalStrength = hashMapOf(EDGE to 80, WIFI to

    90) ...
  36. @PreusslerBerlin ... tested.updateNetworkStatus(networkInfo) assertFalse(tested.isCellularConnected()) assertFalse(tested.isWifiConnected()) ...

  37. @PreusslerBerlin ... tested.updateNetworkStatus(networkInfo) assertFalse(tested.isNotOnAnyNetwork()) ...

  38. @PreusslerBerlin fun ConnectivityReceiver.isNotOnAnyNetwork(): Boolean { return !isCellularConnected() && !isWifiConnected()) }

  39. @PreusslerBerlin fun ConnectivityReceiver.isNotOnAnyNetwork() = !isCellularConnected() && !isWifiConnected())

  40. @PreusslerBerlin @Test fun `should ignore scroll if nothing moved`() {

    tested.setOverScrollListener(scrollListener) tested.onScroll(START_SCROLL, START_SCROLL) verify(scrollListener, never()).onStopped() }
  41. @PreusslerBerlin fun ScrollContainer.onScroll(started : Float, end: Float) { whenever(actionDown.action) .thenReturn(MotionEvent.ACTION_DOWN)

    whenever(actionDown.y) .thenReturn(started) whenever(actionMove.action) .thenReturn(MotionEvent.ACTION_MOVE) … onTouchEventInternal(actionDown) onTouchEventInternal(actionMove) onTouchEventInternal(actionUp) }
  42. @PreusslerBerlin @Test fun `should ignore scroll if nothing moved`() {

    tested.setOverScrollListener(scrollListener) tested.onScroll(START_SCROLL, START_SCROLL) verify(scrollListener, never()).onStopped() }
  43. @PreusslerBerlin ... tested.updateNetworkStatus(networkInfo) tested.assertNotOnAnyNetwork() ...

  44. @PreusslerBerlin fun ConnectivityReceiver.assertNotOnAnyNetwork(){ assertFalse(isConnected()) assertFalse(isWifiConnected()) }

  45. @PreusslerBerlin Make it consistent

  46. @PreusslerBerlin @Test @Throws(NoSuchElementException.class) public void test() { none.get() )

  47. @PreusslerBerlin @Test fun test() { assertFailsWith<NoSuchElementException>( none.get() )

  48. @PreusslerBerlin What about mocks?

  49. @PreusslerBerlin Kotlin Mockito github.com/nhaarman/mockito-kotlin

  50. @PreusslerBerlin Syntactical Sugar around Mockito

  51. @PreusslerBerlin val resources = mock<Resources>()

  52. @PreusslerBerlin val resources : Resources = mock()

  53. @PreusslerBerlin whenever(resources.getString(R.string.title) .thenReturn(”Title")

  54. @PreusslerBerlin val resources = mock<Resources> { on { getString(R.string.title) }

    doReturn ”Title” }
  55. @PreusslerBerlin recyclerview.setAdapter(mock()) * without Java8

  56. @PreusslerBerlin recyclerview.adapter = mock()

  57. @PreusslerBerlin verify(recyclerview).adapter = any()

  58. @PreusslerBerlin InOrder verifier = inOrder(parcel); verifier.verify(parcel).writeByte((byte)0); verifier.verify(parcel).writeString("path");

  59. @PreusslerBerlin inOrder(parcel).apply { verify(parcel).writeByte(0) verify(parcel).writeString("path") }

  60. @PreusslerBerlin ArgumentCaptor<Adapter> captor = ArgumentCaptor.forClass(Adapter.class); verify(recyclerview).setAdapter(captor.capture()); assertEquals(1, captor.getValue().getItemCount());

  61. @PreusslerBerlin argumentCaptor<Adapter<ViewHolder>>().apply { verify(recyclerview).adapter = capture() assertEquals(1, value.itemCount) }

  62. @PreusslerBerlin verify(recyclerView) .adapter = argForWhich { itemCount == 1 }

  63. @PreusslerBerlin verify(parcel) .writeStringArray (argForWhich { size == 2 }) *

    without Java8
  64. @PreusslerBerlin Kluent github.com/MarkusAmshove/Kluent

  65. @PreusslerBerlin Readability first

  66. @PreusslerBerlin assertEquals("hello", greeting);

  67. @PreusslerBerlin greeting shouldEqual "hello"

  68. @PreusslerBerlin greeting `should equal` "hello"

  69. @PreusslerBerlin list shouldContain “jon”

  70. @PreusslerBerlin No magic here

  71. @PreusslerBerlin infix fun <T> Array<T>.shouldContain(theThing: T)

  72. @PreusslerBerlin list.shouldContain(“jon”)

  73. @PreusslerBerlin list shouldContain “jon”

  74. @PreusslerBerlin Verify on mock that mock.getPerson() was called

  75. @PreusslerBerlin theFunction shouldThrow RuntimeException::class

  76. @PreusslerBerlin theFunction `should throw` RuntimeException::class

  77. @PreusslerBerlin shouldThrowTheException IllegalArgumentException::class withCause IOException::class withMessage "hello"

  78. @PreusslerBerlin Junit5

  79. @PreusslerBerlin Class CalculatorTest @Nested @DisplayName(”on addition") class addition { @Test

    @DisplayName(”should return the sum ") fun test() {…} @Nested @DisplayName(”on substraction") class substraction{ … }
  80. @PreusslerBerlin Spek github.com/JetBrains/spek

  81. @PreusslerBerlin SimpleSpec: Spek({ describe("a calculator") { val calculator = SampleCalculator()

    on("addition") { val sum = calculator.sum(2, 4) it("should return the sum") { assertEquals(6, sum) } } } }
  82. @PreusslerBerlin SimpleSpec: Spek({ describe("a calculator") { val calculator = SampleCalculator()

    on("addition") { ... } on(”substraction") { ... } } }
  83. @PreusslerBerlin SimpleSpec: Spek({ describe("a calculator") { val calculator = SampleCalculator()

    on("addition") { ... } on(”substraction") { ... } } describe("a broken calculator") {… }
  84. @PreusslerBerlin describe(...) context(...) given(...) on(...) it(...)

  85. @PreusslerBerlin Tests are a great way to learn Kotlin

  86. @PreusslerBerlin Make a plan how to continue

  87. @PreusslerBerlin Make a plan how to continue •tutorials

  88. @PreusslerBerlin Make a plan how to continue •tutorials •hackathons

  89. @PreusslerBerlin Make a plan how to continue •tutorials •hackathons •coding

    Kata
  90. @PreusslerBerlin Build an experiment, learn from it, see prove it’s

    working
  91. @PreusslerBerlin hackernoon.com/kotlin-in-production-should-you-stay-or-should-you-go-a3428b44b236

  92. @PreusslerBerlin

  93. fun test() Kotlin all your tests Danny Preussler @PreusslerBerlin