TDD on Android (MobOS 2018)

TDD on Android (MobOS 2018)

Slides for my workshop giving in Cluj
Source: https://github.com/sporttotal-tv/android-tdd-workshop

A8b79d304b5184e5a5b0a109590f6683?s=128

Danny Preussler

February 16, 2018
Tweet

Transcript

  1. Testdrive your android app Danny Preussler Head of Mobile

  2. @PreusslerBerlin The source • Please download github.com/ sporttotal-tv/android-tdd-workshop • Branch

    master • Run FirstTest.test() @PreusslerBerlin
  3. @PreusslerBerlin History • Started with test first from XP in

    1999 by Kent Beck • Famous as TDD since Kent Becks book in 2003 • Just a rediscovery: test first was normal in early days of programming
  4. @PreusslerBerlin History • Started with test first from XP in

    1999 by Kent Beck • Famous as TDD since Kent Becks book in 2003 • Just a rediscovery: test first was normal in early days of programming
  5. @PreusslerBerlin History • Started with test first from XP in

    1999 by Kent Beck • Famous as TDD since Kent Becks book in 2003 • Just a rediscovery: test first was normal in early days of programming
  6. @PreusslerBerlin Let’s talk about tests

  7. @PreusslerBerlin “Code without tests is bad code.” “any code without

    test is a legacy code.” (Michael C. Feathers)
  8. @PreusslerBerlin “Code without tests is bad code.” “any code without

    test is legacy code.” (Michael C. Feathers)
  9. @PreusslerBerlin Tests give confidence “how do you know something work

    when you don’t have test for it?” (Robert ‘Uncle Bob’ Martin)
  10. @PreusslerBerlin Tests allow refactoring “Refactoring without good test coverage is

    changing shit” (Martin Fowler)
  11. @PreusslerBerlin Tests are documentation • The only doc that lives

    (with your code) • Test code will become more important than your production code
  12. @PreusslerBerlin About me • Talking about testing for 10y now

    • Its all about discipline • I am a learner like you • Let’s dive in together
  13. @PreusslerBerlin What I learned • ”test after” does not work

    (enough) Class1 Class2 Class3
  14. @PreusslerBerlin What I learned • ”test after” does not work

    (enough) Test1 Test2 Class1 Class2 Class3 Test3
  15. @PreusslerBerlin What I learned • ”test after” does not work

    (enough) Test1 Class1 Class2 Class3
  16. @PreusslerBerlin Look outside the box

  17. @PreusslerBerlin …there is one other [discipline] that is, and that’s

    Accounting. The right mistake … that one-digit error can crash the company and send the offenders off to jail. How do accountants deal with that sensitivity? Well, they have disciplines.
  18. @PreusslerBerlin …there is one other [discipline] that is, and that’s

    Accounting. The right mistake … that one-digit error can crash the company and send the offenders off to jail. How do accountants deal with that sensitivity? Well, they have disciplines.
  19. @PreusslerBerlin …there is one other [discipline] that is, and that’s

    Accounting. The right mistake … that one-digit error can crash the company and send the offenders off to jail. How do accountants deal with that sensitivity? Well, they have disciplines.
  20. @PreusslerBerlin Dual entry book keeping Everything is said twice. Every

    transaction is entered two times — once on the credit side and once on the debit side. Those two transactions follow separate mathematical pathways until they end up at this wonderful subtraction on the balance sheet that has to yield to zero.
  21. @PreusslerBerlin Dual entry book keeping Everything is said twice. Every

    transaction is entered two times — once on the credit side and once on the debit side. Those two transactions follow separate mathematical pathways until they end up at this wonderful subtraction on the balance sheet that has to yield to zero.
  22. @PreusslerBerlin Dual entry book keeping Everything is said twice. Every

    transaction is entered two times — once on the credit side and once on the debit side. Those two transactions follow separate mathematical pathways until they end up at this wonderful subtraction on the balance sheet that has to yield to zero.
  23. @PreusslerBerlin Dual entry book keeping This is what test-driven development

    is: dual-entry bookkeeping. Everything is said twice — once on the test side and once on the production code side and everything runs in an execution that yields either a green bar or a red bar just like the zero on the balance sheet.
  24. @PreusslerBerlin Dual entry book keeping This is what test-driven development

    is: dual-entry bookkeeping. Everything is said twice — once on the test side and once on the production code side and everything runs in an execution that yields either a green bar or a red bar just like the zero on the balance sheet.
  25. @PreusslerBerlin Dual entry book keeping This is what test-driven development

    is: dual-entry bookkeeping. Everything is said twice — once on the test side and once on the production code side and everything runs in an execution that yields either a green bar or a red bar just like the zero on the balance sheet.
  26. @PreusslerBerlin “If you’re doing test-driven development well, you’ll never write

    comments in your code because your tests are a form of documentation for how your program should work,” (Alex Clark, Codecademy)
  27. @PreusslerBerlin How does it work?

  28. @PreusslerBerlin The 3 rules of TDD • You must write

    a failing test before you write any production code. • You must not write more of a test than is sufficient to fail, or fail to compile. • You must not write more production code than is sufficient to make the currently failing test pass. nano-cycle (seconds)
  29. @PreusslerBerlin The 3 rules of TDD • You must write

    a failing test before you write any production code. • You must not write more of a test than is sufficient to fail, or fail to compile. • You must not write more production code than is sufficient to make the currently failing test pass. nano-cycle (seconds)
  30. @PreusslerBerlin The 3 rules of TDD • You must write

    a failing test before you write any production code. • You must not write more of a test than is sufficient to fail, or fail to compile. • You must not write more production code than is sufficient to make the currently failing test pass. nano-cycle (seconds)
  31. @PreusslerBerlin Red Green Refactor • Make it fail • Make

    it work • Make it right micro-cycle (minutes)
  32. @PreusslerBerlin Red Green Refactor • Create a unit tests that

    fails • Write just enough production code to makes that test pass. • Clean up the mess you just made. micro-cycle (minutes)
  33. @PreusslerBerlin Red Green Refactor • Create a unit tests that

    fails • Write just enough production code to makes that test pass. • Clean up the mess you just made. micro-cycle (minutes)
  34. @PreusslerBerlin Red Green Refactor • Create a unit tests that

    fails • Write just enough production code to makes that test pass. • Clean up the mess you just made. micro-cycle (minutes)
  35. @PreusslerBerlin WTF

  36. @PreusslerBerlin Babysteps The philosophy is based on the idea that

    our limited minds are not capable of pursuing the two simultaneous goals of all software systems: 1. Correct behavior. 2. Correct structure.
  37. @PreusslerBerlin Red Green Refactor • Always be one step away

    from green bar • Think of new test-> write it down • It’s getting ugly? -> write it down
  38. @PreusslerBerlin Why? • YAGNI and KISS out of the box

    • eliminate debugging
  39. @PreusslerBerlin Why? • Test all business needs • Less coupled

    to implementation • Less mocking • Forces small changes • TDD is more about design
  40. @PreusslerBerlin Isn’t that slow? https://pixabay.com/en/snail-illustration-drawing-yellow-1757756/

  41. @PreusslerBerlin Isn’t it slow? Writing tests is slower than not

    writing tests. You’ll write at least as much test code as production code TDD adds 10% — 30% on initial costs = longer to complete their projects
  42. @PreusslerBerlin Isn’t it slow? Writing tests is slower than not

    writing tests. You’ll write at least as much test code as production code TDD adds 10% — 30% on initial costs = longer to complete their projects
  43. @PreusslerBerlin Isn’t it slow? Research shows that TDD: • Reduces

    defect density by 60-90 % • Reduces production bug density by 40–80%
  44. @PreusslerBerlin Isn’t it slow? Without TDD, you spend a few

    weeks writing code which mostly works and spend the next year "testing" and fixing many (but not all) of the bugs With TDD, you spend a year writing code which actually works. Then you do final integration testing for a few weeks.
  45. @PreusslerBerlin Isn’t it slow? • Feature takes longer • You

    write at least as much test code as production code
  46. @PreusslerBerlin Isn’t it slow? • Bugfixing phase is shorter •

    Debugging disappears • Ci finds bugs before tester does • Long term it’s much faster no more big rewrite
  47. @PreusslerBerlin Let’s code! • Goal: countdown app @PreusslerBerlin

  48. @PreusslerBerlin Live coding • The countdown @PreusslerBerlin

  49. @PreusslerBerlin Live coding • Result branch tdd_checkpoint_001 @PreusslerBerlin

  50. @PreusslerBerlin Let’s talk Android

  51. violating the most basics principles of Software Engineering! flickr.com/photos/cobblucas/4831501753, Stop

    by Lucas Cobb, CC by 2.0
  52. @PreusslerBerlin TDD and architecture • TDD does not replace Architecture

    and Design • Have a vision in your head • NO big up front design • Defer architecture decisions as long as possible
  53. @PreusslerBerlin TDD and architecture • TDD does not replace Architecture

    and Design • Have a vision in your head • NO big up front design • Defer architecture decisions as long as possible
  54. @PreusslerBerlin TDD and architecture • TDD does not replace Architecture

    and Design • Have a vision in your head • No big up front design • Defer architecture decisions as long as possible
  55. @PreusslerBerlin TDD and architecture • TDD does not replace Architecture

    and Design • Have a vision in your head • NO big up front design • Defer architecture decisions as long as possible
  56. @PreusslerBerlin TDD and architecture Write the tests that forces you

    to write the code you want to write Tip: Create a list of tests on paper
  57. @PreusslerBerlin Separation of concerns (1974)

  58. @PreusslerBerlin D.R.Y.

  59. @PreusslerBerlin Violations of DRY are typically referred to as WET

    solutions, which is commonly taken to stand for either "write everything twice" or "we enjoy typing” (http://en.wikipedia.org/wiki/Don't_repeat_yourself)
  60. @PreusslerBerlin

  61. @PreusslerBerlin Clean Code

  62. @PreusslerBerlin S.O.L.I.D.

  63. @PreusslerBerlin View architecture

  64. @PreusslerBerlin MVP View Presenter Model

  65. @PreusslerBerlin MVVM View ViewModel Model

  66. @PreusslerBerlin Live coding • viewmodel @PreusslerBerlin

  67. @PreusslerBerlin Live coding • Result branch tdd_checkpoint_002 @PreusslerBerlin

  68. @PreusslerBerlin Lets talk about Activities • now the tricky part

    starts https://www.flickr.com/photos/muchadoaboutnothing/438734626
  69. @PreusslerBerlin Android SDK under test • Android classes can be

    loaded on JVM: build/generated/mockable-android-XX.jar • No more finals!
  70. @PreusslerBerlin Android SDK under test • Empty methods with default

    return values android { … testOptions { unitTests.returnDefaultValues = true } -> no code will run
  71. @PreusslerBerlin Live coding • Classic Activity @PreusslerBerlin

  72. @PreusslerBerlin Live coding • Result branch tdd_checkpoint_003a @PreusslerBerlin

  73. @PreusslerBerlin Dependency Injection

  74. @PreusslerBerlin Dependency Inversion Inversion of Control Dependency Injection ? ?

    ?
  75. @PreusslerBerlin The Dependency Inversion Principle High level entities should not

    depend on low level details.
  76. @PreusslerBerlin Inversion of Control Who initiates a message Hollywood's Law:

    don't call me, I'll call you.
  77. @PreusslerBerlin Inversion of Control Stop using new

  78. @PreusslerBerlin Inversion of Control Common implementations: • Factory • Service

    Locator • Dependency Injection
  79. @PreusslerBerlin Inversion of Control Common implementations: • Factory tracker =

    Factory.createTracker() • Service Locator • Dependency Injection
  80. @PreusslerBerlin Inversion of Control Common implementations: • Factory • Service

    Locator tracker = Locator.get(Tracker.class) • Dependency Injection
  81. @PreusslerBerlin Inversion of Control Common implementations: • Factory • Service

    Locator • Dependency Injection @Inject Tracker tracker;
  82. @PreusslerBerlin Live coding • Activity with injection @PreusslerBerlin

  83. @PreusslerBerlin Live coding • Result branch tdd_checkpoint_003b @PreusslerBerlin

  84. @PreusslerBerlin Rotation

  85. @PreusslerBerlin Rotation • Many different solutions • Let’s go with

    view model from Architecture Components
  86. @PreusslerBerlin Rotation • Many different solutions • Let’s go with

    view model from Architecture Components
  87. @PreusslerBerlin life cycle: rotation onCreate onStart onResume onPause onStop onDestroy

    onCreate onStart onResume ViewModel
  88. @PreusslerBerlin Problem • Needs a FragmentActivity public static ViewModelProvider of(FragmentActivity

    activity) { L
  89. @PreusslerBerlin The problem of v4… Activity • mockable.jar not existing

    for libraries, including support library L -> real code will run
  90. @PreusslerBerlin Live coding • FragmentActivity @PreusslerBerlin

  91. @PreusslerBerlin Live coding • Result branch tdd_checkpoint_004a @PreusslerBerlin

  92. @PreusslerBerlin Live coding • ViewModel @PreusslerBerlin

  93. @PreusslerBerlin Live coding • Result branch tdd_checkpoint_004b @PreusslerBerlin

  94. @PreusslerBerlin What’s left? • Activities needs to be declared in

    manifest • Permissions for older devices (internet)
  95. @PreusslerBerlin Live coding • Manifest via lint lintOptions { check

    'Registered' warningsAsErrors true } * Has issues with Gradle Plugin 3.0 and Kotlin @PreusslerBerlin
  96. @PreusslerBerlin Live coding • Manifest via lint

  97. @PreusslerBerlin Live coding • Result branch tdd_checkpoint_005 @PreusslerBerlin

  98. @PreusslerBerlin Live coding • Zero tolerance policy very valuable

  99. @PreusslerBerlin What’s left? • Permissions for older devices (internet)

  100. @PreusslerBerlin An alternative

  101. @PreusslerBerlin Lets talk about data binding • Testing android view

    classes are hard • So let’s make sure we don’t need to test them!
  102. @PreusslerBerlin Data binding full picture XML ViewModel bind

  103. @PreusslerBerlin Data binding… who is the view? • Solves the

    one big android question once and for all
  104. @PreusslerBerlin ViewModel in Data Binding <TextView … android:id="@+id/all_shows_item_title" android:text="@{viewModel.title}" />

    <data> <variable name="viewModel" type=”tv.sporttotal. ViewModel"/> </data>
  105. @PreusslerBerlin ViewModel in data binding <android.support.v7.widget.CardView … android:onClick="@{() -> viewModel.onClicked()}">

  106. @PreusslerBerlin Live coding • Data binding @PreusslerBerlin

  107. @PreusslerBerlin Live coding • Result branch tdd_checkpoint_006 @PreusslerBerlin

  108. @PreusslerBerlin How do I…

  109. @PreusslerBerlin .. show a toast class SeriesViewModel : Viewmodel() {

    … @Bindable var error = ObservableField<String>()
  110. @PreusslerBerlin .. show a toast viewModel.error.addOnPropertyChangedCallback( object : OnPropertyChangedCallback() {

    override fun onPropertyChanged(…) { showToast(viewModel.error.get() } })
  111. @PreusslerBerlin .. show a toast (alternative) <FrameLayout app:showError="@{viewModel.errorAppeared}"> @BindingAdapter("showError") fun

    ViewGroup.onErrorAppeared(error: String?){ errorString?.let { showToast(context, error)) } }
  112. @PreusslerBerlin Data binding full picture XML Activity ViewModel (un)bind bind

    Life cycle aware class (un)bind
  113. @PreusslerBerlin Whats left? • UI: text size, button text…

  114. @PreusslerBerlin What about Espresso • problem: • need to be

    fast! • need to run continuously • fast feedback
  115. @PreusslerBerlin Live coding • Espresso @PreusslerBerlin

  116. @PreusslerBerlin Live coding • Result branch tdd_checkpoint_007 @PreusslerBerlin

  117. @PreusslerBerlin What about Robolectric?

  118. @PreusslerBerlin Tired of issues like java.lang.NullPointerException at org.robolectric.manifest.MetaData. init(MetaData.java:55) at

    org.robolectric.manifest.AndroidMa nifest.initMetaData(AndroidManifes t.java:377).... ? Sleepy by Tomas; flickr.com/photos/tma/2438467223; CC 2.0 Don’t spent more time fixing your test setup than fixing your app Sleepy by Tomas; flickr.com/photos/tma/2438467223; CC 2.0
  119. @PreusslerBerlin What’s wrong with Robolectric? • Developers used too much

    magic forgot what unit test should be • Your tests rely on correct 3rd party implementation • Tests itself becomes flaky • It’s slower than pure JUnit
  120. @PreusslerBerlin Android today • Projects not designed to be testable

    have often need for Robolectric
  121. @PreusslerBerlin Time to wrap up

  122. @PreusslerBerlin Limitations • Custom view and animation code might get

    too hard
  123. @PreusslerBerlin Prototypes, POC and MVPs • For prototypes: hacking together

    is fine But after: start from scratch and build it test driven • For MVP: an MVP is a minimum feature set but no excuse for bad quality
  124. @PreusslerBerlin "Test only if you would want it to work.”

    Kent Beck
  125. @PreusslerBerlin If it's worth building, it's worth testing If it's

    not worth testing, why are you wasting your time working on it?
  126. @PreusslerBerlin If it's worth building, it's worth testing If it's

    not worth testing, why are you wasting your time working on it?
  127. @PreusslerBerlin Remember! • Never write new functionality without test first

    • Tests need to be refactored and clean!
  128. @PreusslerBerlin TDD on Android? • its possible! • its fun!

    https://www.flickr.com/photos/chefranden/14838138493
  129. @PreusslerBerlin TDD on Android? Unit tests (even with TDD) are

    NOT the only tests you need
  130. @PreusslerBerlin Tools used • github.com/MarkusAmshove/Kluent • github.com/nhaarman/mockito-kotlin • Final mocking

    with mockito2: github.com/mockito/mockito/wiki/What%2 7s-new-in-Mockito-2#mock-the- unmockable-opt-in-mocking-of-final- classesmethods
  131. @PreusslerBerlin Tools used • Protected onCreate call in Kotlin: github.com/

    dpreussler/android-tdd-utils • Test friendly dependency Injection: github.com/ stephanenicolas/toothpick
  132. @PreusslerBerlin Tools used • Some test utils code for FragmentActivity,

    check TestUtils.kt github.com/ sporttotal-tv/android-tdd-workshop
  133. @PreusslerBerlin More resources • Test Driven Development by Example (Kent

    Beck) • https://cleancoders.com/videos • https://online-training.jbrains.ca/p/wbitdd-01 • http://news.codecademy.com/test-driven- development/
  134. Testdrive your android app @PreusslerBerlin