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

Pragmatic Testing

Pragmatic Testing

(given at Droidcon Italy 2018 and Android Makers 2018)

You open a pull request only to find that some tests are now failing. What goes through your mind?

Did I break something? Do I need to update the tests? Just what is that test testing anyway?

Ideally, #2 would almost never be the case, as rewriting a test usually means it's lost the regression testing value of its original form. How can we get there though? There’s no one right way to write tests, and it’s surprisingly easy to fall into the trap of writing change tests. There are good patterns you can leverage to scale them and maximize their long term value. This talk will detail simple patterns you can follow for healthy, pragmatic testing with a focus on test design, correctness, and how these values can translate into better overall API design of your code.

85a1166e93654865b4dcafdafe2b2dfd?s=128

Zac Sweers

April 24, 2018
Tweet

More Decks by Zac Sweers

Other Decks in Programming

Transcript

  1. Pragmatic Testing Zac Sweers - Uber @pandanomic

  2. Testing?

  3. Testing!

  4. @Test

  5. @Test

  6. Pragmatic Testing

  7. Pragmatic? Testing

  8. Goals ⚽

  9. Non-Goals

  10. Non-Goals • Convince you testing is worthwhile

  11. Non-Goals • Convince you testing is worthwhile • UI Testing

    (Espresso, layouts, etc)
  12. Non-Goals • Convince you testing is worthwhile • UI Testing

    (Espresso, layouts, etc) • Knock specific libraries
  13. None
  14. Boundaries

  15. Calculator

  16. Calculator add() subtract() multiply() divide() equal() clear()

  17. Calculator InputManager DisplayManager CPU RAM add() subtract() multiply() divide() equal()

    clear()
  18. InputManager DisplayManager CPU RAM add() subtract() multiply() divide() equal() clear()

  19. InputManager DisplayManager CPU RAM add() subtract() multiply() divide() equal() clear()

  20. InputManager DisplayManager CPU RAM add() subtract() multiply() divide() equal() clear()

  21. InputManager DisplayManager CPU RAM add() subtract() multiply() divide() equal() clear()

  22. InputManager DisplayManager CPU RAM

  23. InputManager DisplayManager CPU RAM

  24. @Test fun testAdd() { }a InputManager DisplayManager CPU RAM

  25. @Test fun testAdd() { val calculator = Calculator() }a InputManager

    DisplayManager CPU RAM
  26. @Test fun testAdd() { val cpu = mock(CPU::class.java) val calculator

    = Calculator() }a InputManager DisplayManager CPU RAM
  27. @Test fun testAdd() { val cpu = mock(CPU::class.java) val ram

    = mock(RAM::class.java) val calculator = Calculator() }a InputManager DisplayManager CPU RAM
  28. @Test fun testAdd() { val cpu = mock(CPU::class.java) val ram

    = mock(RAM::class.java) val display = mock(DisplayManager::class.java) val calculator = Calculator() }a InputManager DisplayManager CPU RAM
  29. @Test fun testAdd() { val cpu = mock(CPU::class.java) val ram

    = mock(RAM::class.java) val display = mock(DisplayManager::class.java) val input = mock(InputManager::class.java) val calculator = Calculator() }a InputManager DisplayManager CPU RAM
  30. @Test fun testAdd() { val cpu = mock(CPU::class.java) val ram

    = mock(RAM::class.java) val display = mock(DisplayManager::class.java) val input = mock(InputManager::class.java) val calculator = Calculator(cpu, ram, display, input) }a InputManager DisplayManager CPU RAM
  31. @Test fun testAdd() { // Mocks... val calculator = Calculator(cpu,

    ram, display, input) }a InputManager DisplayManager CPU RAM
  32. @Test fun testAdd() { // Mocks... val calculator = Calculator(cpu,

    ram, display, input) input.add(2, 2) }a InputManager DisplayManager CPU RAM
  33. @Test fun testAdd() { // Mocks... val calculator = Calculator(cpu,

    ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) }a InputManager DisplayManager CPU RAM
  34. @Test fun testAdd() { // Mocks... val calculator = Calculator(cpu,

    ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) }a InputManager DisplayManager CPU RAM
  35. @Test fun testAdd() { // Mocks... val calculator = Calculator(cpu,

    ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) }a InputManager DisplayManager CPU RAM
  36. @Test fun testAdd() { // Mocks... val calculator = Calculator(cpu,

    ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(input).onInputReceived("+") }a InputManager DisplayManager CPU RAM
  37. @Test fun testAdd() { // Mocks... val calculator = Calculator(cpu,

    ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(input).onInputReceived("+") verify(ram).storeInRAM(2) }a InputManager DisplayManager CPU RAM
  38. @Test fun testAdd() { // Mocks... val calculator = Calculator(cpu,

    ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") }a InputManager DisplayManager CPU RAM
  39. @Test fun testAdd() { // Mocks... val calculator = Calculator(cpu,

    ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") verify(input).onInputReceived(2) }a InputManager DisplayManager CPU RAM
  40. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) }a
  41. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() }a
  42. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() verify(cpu).calculateSum(2, 2) }a
  43. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() verify(cpu).calculateSum(2, 2) verify(display).displayResult(4) }a
  44. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) input.add(2, 2) verify(cpu).setRam(ram) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() verify(cpu).calculateSum(2, 2) verify(display).displayResult(4) }a
  45. @Test fun testAdd() { input.add(2, 2) }a InputManager DisplayManager CPU

    RAM
  46. @Test fun testAdd() { input.add(2, 2) }a registerInputListener(listener: Listener) InputManager

    DisplayManager CPU RAM
  47. val captor = ArgumentCaptor.forClass(Listener::class.java) InputManager DisplayManager CPU RAM

  48. val captor = ArgumentCaptor.forClass(Listener::class.java) verify(input).registerInputListener(captor.capture()) InputManager DisplayManager CPU RAM

  49. val captor = ArgumentCaptor.forClass(Listener::class.java) verify(input).registerInputListener(captor.capture()) val listener = captor.value InputManager

    DisplayManager CPU RAM
  50. @Test fun testAdd() { val captor = ArgumentCaptor.forClass(Listener::class.java) verify(input).registerInputListener(captor.capture()) val

    listener = captor.value input.add(2, 2) }a InputManager DisplayManager CPU RAM
  51. @Test fun testAdd() { // Captor stuff input.add(2, 2) }a

    InputManager DisplayManager CPU RAM
  52. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) // Captor stuff... input.add(2, 2) verify(cpu).setRam(ram) listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) listener.onNewInput("+") verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() verify(cpu).calculateSum(2, 2) verify(display).displayResult(4) }a
  53. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) // Captor stuff... input.add(2, 2) verify(cpu).setRam(ram) listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) listener.onNewInput("+") verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() verify(cpu).calculateSum(2, 2) verify(display).displayResult(4) }a
  54. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) // Captor stuff... input.add(2, 2) verify(cpu).setRam(ram) listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) listener.onNewInput("+") verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() verify(cpu).calculateSum(2, 2) verify(display).displayResult(4) }a
  55. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) // Captor stuff... input.add(2, 2) verify(cpu).setRam(ram) listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) listener.onNewInput("+") verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() verify(cpu).calculateSum(2, 2) verify(display).displayResult(4) }a
  56. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) // Captor stuff... input.add(2, 2) verify(cpu).setRam(ram) listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) listener.onNewInput("+") verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() verify(cpu).calculateSum(2, 2) verify(display).displayResult(4) }a
  57. @Test fun testAdd() { verify(cpu).calculateSum(2, 2) verify(display).displayResult(4) }a InputManager DisplayManager

    CPU RAM
  58. @Test fun testAdd() { verify(cpu).calculateSum(2, 2) // Actually 0 verify(display).displayResult(4)

    }a InputManager DisplayManager CPU RAM
  59. @Test fun testAdd() { when(cpu.calculateSum(2, 2)).thenReturn(4) verify(cpu).calculateSum(2, 2) verify(display).displayResult(4) }a

    InputManager DisplayManager CPU RAM
  60. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) // Captor stuff... input.add(2, 2) verify(cpu).setRam(ram) listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) listener.onNewInput("+") verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() when(cpu.calculateSum(2, 2)).thenReturn(4) verify(cpu).calculateSum(2, 2) verify(display).displayResult(4)
  61. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) // Captor stuff... input.add(2, 2) verify(cpu).setRam(ram) listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) listener.onNewInput("+") verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() when(cpu.calculateSum(2, 2)).thenReturn(4) verify(cpu).calculateSum(2, 2) verify(display).displayResult(4)
  62. InputManager DisplayManager CPU RAM @Test fun testAdd() { // Mocks...

    val calculator = Calculator(cpu, ram, display, input) // Captor stuff... input.add(2, 2) verify(cpu).setRam(ram) listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) listener.onNewInput("+") verify(input).onInputReceived("+") verify(ram).storeInRAM(2) verify(cpu).loadInputToCpu("+") listener.onNewInput(2) verify(input).onInputReceived(2) verify(cpu).loadInputToCpu(2) verify(ram).loadFromRam() when(cpu.calculateSum(2, 2)).thenReturn(4) verify(cpu).calculateSum(2, 2) verify(display).displayResult(4)
  63. @Test fun testAdd() { // Mocks... val calculator = Calculator(cpu,

    ram, display, input) // Captor stuff... input.add(2, 2) listener.onNewInput(2) listener.onNewInput("+") listener.onNewInput(2) when(cpu.calculateSum(2, 2)).thenReturn(4) verify(cpu).calculateSum(2, 2) verify(display).displayResult(4) }a InputManager DisplayManager CPU RAM
  64. InputManager DisplayManager CPU RAM @Test fun testAdd() { val calculator

    = Calculator(cpu, ram, display, input) }a
  65. InputManager DisplayManager CPU RAM @Test fun testAdd() { val calculator

    = Calculator(cpu, ram, display, input) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  66. InputManager DisplayManager CPU RAM InputManager DisplayManager CPU RAM @Test fun

    testAdd() { val calculator = Calculator(cpu, ram, display, input) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  67. DisplayManager CPU RAM InputManager DisplayManager CPU RAM @Test fun testAdd()

    { val calculator = Calculator(cpu, ram, display, input) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  68. DisplayManager CPU RAM DisplayManager CPU RAM @Test fun testAdd() {

    val calculator = Calculator(cpu, ram, display) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  69. DisplayManager RAM DisplayManager CPU RAM @Test fun testAdd() { val

    calculator = Calculator(cpu, ram, display) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  70. DisplayManager CPU RAM @Test fun testAdd() { val calculator =

    Calculator(CPU(), ram, display) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  71. @Test fun testAdd() { val calculator = Calculator(CPU(), ram, display)

    assertThat(calculator.add(2, 2)).isEqualTo(4) }a DisplayManager CPU RAM DisplayManager CPU RAM
  72. DisplayManager CPU DisplayManager CPU RAM @Test fun testAdd() { val

    calculator = Calculator(CPU(), ram, display) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  73. RAM

  74. RAM interface RAMa{ <T>afunastoreInRAM(key:aString,avalue:aT) <T>afunaloadFromRam(key:aString):aT? }

  75. class InMemoryRAM : RAMa{ private val store = mutableMapOf<String, Any>()

    override <T>afunastoreInRAM(key:aString,avalue:aT) { return store[key] = value } override <T>afunaloadFromRam(key:aString):aT? { return store[key] } }
  76. InMemoryRAM

  77. DisplayManager CPU InMemoryRAM @Test fun testAdd() { val ram =

    mock(RAM::class.java) val calculator = Calculator(CPU(), ram, display) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  78. DisplayManager CPU InMemoryRAM @Test fun testAdd() { val ram =

    InMemoryRAM() val calculator = Calculator(CPU(), ram, display) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  79. CPU DisplayManager CPU @Test fun testAdd() { val ram =

    InMemoryRAM() val calculator = Calculator(CPU(), ram, display) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  80. CPU DisplayManager CPU @Test fun testAdd() { val cpu =

    CPU(InMemoryRAM()) val calculator = Calculator(cpu, display) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  81. CPU CPU DisplayManager @Test fun testAdd() { val cpu =

    CPU(InMemoryRAM()) val calculator = Calculator(cpu, display) assertThat(calculator.add(2, 2)).isEqualTo(4) }a DisplayManager
  82. CPU CPU DisplayManager verify(display).displayResult(4) DisplayManager

  83. CPU CPU DisplayManager verify(display).displayResult(4) assertThat(display.getCurrentResult()).isEqualTo(4) DisplayManager

  84. CPU CPU DisplayManager verify(display).displayResult(4) assertThat(display.getCurrentResult()).isEqualTo(4) assertThat(display.recordedResults[0]).isEqualTo(4) DisplayManager

  85. CPU CPU val resultHandler = RecordingResultHandler() // Calc assertThat(resultHandler.results[0]).isEqualTo(4) ResultHandler

  86. CPU ResultHandler CPU @Test fun testAdd() { val cpu =

    CPU(InMemoryRAM()) val resultHandler = RecordingResultHandler() val calculator = Calculator(cpu, resultHandler) calculator.add(2, 2) assertThat(resultHandler.results[0]).isEqualTo(4) }a
  87. CPU @Test fun testAdd() { val cpu = CPU(InMemoryRAM()) val

    calculator = Calculator(cpu) assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  88. val cpu = CPU(InMemoryRAM()) val calculator = Calculator(cpu) @Test fun

    testAdd() { assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  89. val cpu = CPU(InMemoryRAM()) val calculator = Calculator(cpu) @Testa fun

    testAdd() { assertThat(calculator.add(2, 2)).isEqualTo(4) }a @Testb fun testSub() { assertThat(calculator.subtract(2, 2)).isEqualTo(0) }b @Testc fun testMultiply() { assertThat(calculator.multiply(2, 2)).isEqualTo(4) }c
  90. val cpu = CPU(InMemoryRAM()) val calculator = Calculator(cpu) @Testa fun

    testAdd() { assertThat(calculator.add(2, 2)).isEqualTo(4) }a
  91. val cpu = CPU(InMemoryRAM()) val resultHandler = RecordingResultHandler() val calculator

    = Calculator(cpu, resultHandler) @Test fun testAdd() { calculator.add(2, 2) calculator.add(2, 3) calculator.add(2, -1) assertThat(resultHandler.results) .containsExactly(4, 5, 1) }a
  92. Failure

  93. @Test fun foo() { try { foo.bar() } catch (e:

    Exception) { // Expected } }a
  94. @Test(expected = Exception::class) fun foo() { foo.bar() }a

  95. @Test(expected = ExpectedException::class) fun foo() { foo.bar() }a

  96. @Test() fun foo() { try { foo.bar() } catch (e:

    ExpectedException) { assertThat(e) .hasMessageThat() .contains("Oh no") }b }a
  97. @Test() fun foo() { try { foo.bar() throw AssertionError("No") }

    catch (e: ExpectedException) { assertThat(e) .hasMessageThat() .contains("Oh no") }b }a
  98. RxJava

  99. @Test() fun rx() { Observable.error() .subscribe() }a

  100. @Test() fun rx() { Observable.error() .subscribe() }a

  101. throw error

  102. // throw error Thread currentThread = Thread.currentThread(); UncaughtExceptionHandler handler =

    currentThread.getUncaughtExceptionHandler(); handler.uncaughtException(currentThread, error);
  103. // throw error Thread currentThread = Thread.currentThread(); UncaughtExceptionHandler handler =

    currentThread.getUncaughtExceptionHandler(); handler.uncaughtException(currentThread, error); https://github.com/uber/AutoDispose/blob/master/autodispose/src/test/java/com/uber/ autodispose/RxErrorsRule.java
  104. TestObserver

  105. TestObserver Observable.just(1) .test()

  106. TestObserver Observable.just(1) .test() .assertValue(...)

  107. TestObserver Observable.just(1) .test() .assertValue(...) .assertError(...)

  108. TestObserver Observer<Integer> o = mock(Observer::class.java) Observable.just(1) .subscribe(o) verify(o).onNext(...) verify(o).onError(...)

  109. TestObserver Observable.just(1) .test() .assertValue(...) .assertError(...) .awaitDone(...)

  110. Some others quickly because ⏰

  111. What about my architecture?

  112. class Calculator(val cpu: CPU, val resultHandler: ResultHandler, val inputManager: InputManager)

  113. class CalculatorPresenter(val cpu: CPU, val resultHandler: ResultHandler, val inputManager: InputManager)

  114. class CalculatorPresenter(val cpu: CPU, val resultHandler: ResultHandler, val inputManager: InputManager)

    Courtesy of Nick Butcher (@crafty)
  115. class CalculatorPresenter(val cpu: CPU, val resultHandler: ResultHandler, val inputManager: InputManager)

    0.0 7 8 9 4 5 6 1 2 3 C 0 ⏎
  116. class CurrentValueView : ResultHandler 0.0 7 8 9 4 5

    6 1 2 3 C 0 ⏎
  117. class NumberInputManager : InputManager class CurrentValueView : ResultHandler 0.0 7

    8 9 4 5 6 1 2 3 C 0 ⏎
  118. class NumberInputManager : InputManager class CurrentValueView : ResultHandler 0.0 7

    8 9 4 5 6 1 2 3 C 0 ⏎ class ImeInputManager : InputManager
  119. class NumberInputManager : InputManager class CurrentValueView : ResultHandler 7 8

    9 4 5 6 1 2 3 C 0 ⏎ 0.0
  120. What about DI?

  121. class MainActivity : Activity { @Inject lateinit var foo: Foo

    @Inject lateinit var bar: Bar }
  122. @Test fun testActivity() { val activity = ... activity.foo =

    TestFoo() activity.bar = MockBar() }
  123. @Test fun testActivity() { val activity = MainActivityTestHelper.create( TestFoo(), MockBar()

    )a }b
  124. @Test fun testActivity() { val activity = MainActivityTestHelper.create( TestFoo(), MockBar()

    )a }b RIBS: https://github.com/uber/RIBs/blob/master/android/libraries/rib- compiler-test/src/main/java/com/uber/rib/compiler/ InteractorTestGenerator.java Injection Helper: https://github.com/dinosaurwithakatana/injection- helper
  125. What about state?

  126. State machines!

  127. In short

  128. Value is the boundary

  129. Value is the boundary assertThat(calculator.add(2, 2)).isEqualTo(4) assertThat(resultHandler.results[0]).isEqualTo(4) Observable.just(1) .test() .assertValue(...)

    .assertError(...) .awaitDone(...)
  130. Fakes are worth writing

  131. Fakes are worth writing class InMemoryRAM : RAM { private

    val store = mutableMapOf<String, Any>() override <T> fun storeInRAM(key: String, value: T) { return store[key] = value } override <T> fun loadFromRam(key: String): T? { return store[key] } }
  132. Ship your fakes

  133. Ship your fakes fun test(): TestObserver<T> TestScopeProvider Room.inMemoryDatabaseBuilder(...)

  134. Tests should test the public API

  135. Tests should test the public API Semver your tests!

  136. Test are prod code

  137. Failures should be designed for

  138. Failures should be designed for throw AssertionError("Should have crashed before

    this") catch (e: ExpectedException) { assertThat(e).isWhatWeExpected() } @Rule val errorsRule = RxErrorsRule()
  139. More than one right way

  140. Keep it simple.

  141. Keep it pragmatic.

  142. Further reading • Boundaries: https:// www.destroyallsoftware.com/talks/boundaries • Square libraries •

    RxJava • AutoDispose
  143. Fini, merci! Zac Sweers - Uber @pandanomic