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

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

Danny Preussler

February 16, 2018
Tweet

More Decks by Danny Preussler

Other Decks in Technology

Transcript

  1. Testdrive your
    android app
    Danny Preussler
    Head of Mobile

    View Slide

  2. @PreusslerBerlin
    The source
    • Please download
    github.com/
    sporttotal-tv/android-tdd-workshop
    • Branch
    master
    • Run
    FirstTest.test()
    @PreusslerBerlin

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  6. @PreusslerBerlin
    Let’s
    talk about
    tests

    View Slide

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

    View Slide

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

    View Slide

  9. @PreusslerBerlin
    Tests give confidence
    “how do you know something work when you
    don’t have test for it?”
    (Robert ‘Uncle Bob’ Martin)

    View Slide

  10. @PreusslerBerlin
    Tests allow refactoring
    “Refactoring without good test coverage
    is changing shit”
    (Martin Fowler)

    View Slide

  11. @PreusslerBerlin
    Tests are documentation
    • The only doc that lives (with your code)
    • Test code will become more important than
    your production code

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. @PreusslerBerlin
    Look
    outside
    the box

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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)

    View Slide

  27. @PreusslerBerlin
    How does
    it work?

    View Slide

  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)

    View Slide

  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)

    View Slide

  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)

    View Slide

  31. @PreusslerBerlin
    Red Green Refactor
    • Make it fail
    • Make it work
    • Make it right
    micro-cycle (minutes)

    View Slide

  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)

    View Slide

  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)

    View Slide

  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)

    View Slide

  35. @PreusslerBerlin
    WTF

    View Slide

  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.

    View Slide

  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

    View Slide

  38. @PreusslerBerlin
    Why?
    • YAGNI and KISS out of the box
    • eliminate debugging

    View Slide

  39. @PreusslerBerlin
    Why?
    • Test all business needs
    • Less coupled to implementation
    • Less mocking
    • Forces small changes
    • TDD is more about design

    View Slide

  40. @PreusslerBerlin
    Isn’t that
    slow?
    https://pixabay.com/en/snail-illustration-drawing-yellow-1757756/

    View Slide

  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

    View Slide

  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

    View Slide

  43. @PreusslerBerlin
    Isn’t it slow?
    Research shows that TDD:
    • Reduces defect density by 60-90 %
    • Reduces production bug density by 40–80%

    View Slide

  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.

    View Slide

  45. @PreusslerBerlin
    Isn’t it slow?
    • Feature takes longer
    • You write at least as much test code as
    production code

    View Slide

  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

    View Slide

  47. @PreusslerBerlin
    Let’s code!
    • Goal: countdown app
    @PreusslerBerlin

    View Slide

  48. @PreusslerBerlin
    Live coding
    • The countdown
    @PreusslerBerlin

    View Slide

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

    View Slide

  50. @PreusslerBerlin
    Let’s
    talk
    Android

    View Slide

  51. violating
    the most basics
    principles
    of Software Engineering!
    flickr.com/photos/cobblucas/4831501753, Stop by Lucas Cobb, CC by 2.0

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  57. @PreusslerBerlin
    Separation of
    concerns
    (1974)

    View Slide

  58. @PreusslerBerlin
    D.R.Y.

    View Slide

  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)

    View Slide

  60. @PreusslerBerlin

    View Slide

  61. @PreusslerBerlin
    Clean
    Code

    View Slide

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

    View Slide

  63. @PreusslerBerlin
    View
    architecture

    View Slide

  64. @PreusslerBerlin
    MVP
    View Presenter Model

    View Slide

  65. @PreusslerBerlin
    MVVM
    View ViewModel Model

    View Slide

  66. @PreusslerBerlin
    Live coding
    • viewmodel
    @PreusslerBerlin

    View Slide

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

    View Slide

  68. @PreusslerBerlin
    Lets talk about Activities
    • now the tricky part
    starts
    https://www.flickr.com/photos/muchadoaboutnothing/438734626

    View Slide

  69. @PreusslerBerlin
    Android SDK under test
    • Android classes can be loaded on JVM:
    build/generated/mockable-android-XX.jar
    • No more finals!

    View Slide

  70. @PreusslerBerlin
    Android SDK under test
    • Empty methods with default return values
    android {

    testOptions {
    unitTests.returnDefaultValues = true
    }
    -> no code will run

    View Slide

  71. @PreusslerBerlin
    Live coding
    • Classic Activity
    @PreusslerBerlin

    View Slide

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

    View Slide

  73. @PreusslerBerlin
    Dependency
    Injection

    View Slide

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

    View Slide

  75. @PreusslerBerlin
    The Dependency Inversion Principle
    High level entities
    should not depend on
    low level details.

    View Slide

  76. @PreusslerBerlin
    Inversion of Control
    Who initiates a message
    Hollywood's Law:
    don't call me, I'll call you.

    View Slide

  77. @PreusslerBerlin
    Inversion of Control
    Stop using new

    View Slide

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

    Dependency Injection

    View Slide

  79. @PreusslerBerlin
    Inversion of Control
    Common implementations:
    ● Factory
    tracker = Factory.createTracker()
    ● Service Locator
    ● Dependency Injection

    View Slide

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

    View Slide

  81. @PreusslerBerlin
    Inversion of Control
    Common implementations:
    ● Factory
    ● Service Locator
    ● Dependency Injection
    @Inject Tracker tracker;

    View Slide

  82. @PreusslerBerlin
    Live coding
    • Activity with injection
    @PreusslerBerlin

    View Slide

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

    View Slide

  84. @PreusslerBerlin
    Rotation

    View Slide

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

    View Slide

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

    View Slide

  87. @PreusslerBerlin
    life cycle: rotation
    onCreate
    onStart
    onResume
    onPause
    onStop
    onDestroy
    onCreate
    onStart
    onResume
    ViewModel

    View Slide

  88. @PreusslerBerlin
    Problem
    • Needs a FragmentActivity
    public static ViewModelProvider
    of(FragmentActivity activity) {
    L

    View Slide

  89. @PreusslerBerlin
    The problem of v4… Activity
    • mockable.jar not existing for libraries,
    including support library L
    -> real code will run

    View Slide

  90. @PreusslerBerlin
    Live coding
    • FragmentActivity
    @PreusslerBerlin

    View Slide

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

    View Slide

  92. @PreusslerBerlin
    Live coding
    • ViewModel
    @PreusslerBerlin

    View Slide

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

    View Slide

  94. @PreusslerBerlin
    What’s left?
    • Activities needs to be declared in manifest
    • Permissions for older devices (internet)

    View Slide

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

    View Slide

  96. @PreusslerBerlin
    Live coding
    • Manifest via lint

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  100. @PreusslerBerlin
    An
    alternative

    View Slide

  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!

    View Slide

  102. @PreusslerBerlin
    Data binding full picture
    XML ViewModel
    bind

    View Slide

  103. @PreusslerBerlin
    Data binding…
    who is the view?
    • Solves the one
    big android question once
    and for all

    View Slide

  104. @PreusslerBerlin
    ViewModel in Data Binding

    android:id="@+id/all_shows_item_title"
    android:text="@{viewModel.title}" />

    name="viewModel"
    type=”tv.sporttotal. ViewModel"/>

    View Slide

  105. @PreusslerBerlin
    ViewModel in data binding

    android:onClick="@{() -> viewModel.onClicked()}">

    View Slide

  106. @PreusslerBerlin
    Live coding
    • Data binding
    @PreusslerBerlin

    View Slide

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

    View Slide

  108. @PreusslerBerlin
    How do I…

    View Slide

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

    @Bindable
    var error = ObservableField()

    View Slide

  110. @PreusslerBerlin
    .. show a toast
    viewModel.error.addOnPropertyChangedCallback(
    object : OnPropertyChangedCallback() {
    override fun onPropertyChanged(…) {
    showToast(viewModel.error.get()
    }
    })

    View Slide

  111. @PreusslerBerlin
    .. show a toast (alternative)
    app:showError="@{viewModel.errorAppeared}">
    @BindingAdapter("showError")
    fun ViewGroup.onErrorAppeared(error: String?){
    errorString?.let {
    showToast(context, error))
    }
    }

    View Slide

  112. @PreusslerBerlin
    Data binding full picture
    XML
    Activity ViewModel
    (un)bind
    bind
    Life cycle
    aware class (un)bind

    View Slide

  113. @PreusslerBerlin
    Whats left?
    • UI: text size, button text…

    View Slide

  114. @PreusslerBerlin
    What about Espresso
    • problem:
    • need to be fast!
    • need to run continuously
    • fast feedback

    View Slide

  115. @PreusslerBerlin
    Live coding
    • Espresso
    @PreusslerBerlin

    View Slide

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

    View Slide

  117. @PreusslerBerlin
    What about
    Robolectric?

    View Slide

  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

    View Slide

  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

    View Slide

  120. @PreusslerBerlin
    Android today
    • Projects not designed to be testable
    have often need for Robolectric

    View Slide

  121. @PreusslerBerlin
    Time to
    wrap up

    View Slide

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

    View Slide

  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

    View Slide

  124. @PreusslerBerlin
    "Test only if you would
    want it to work.”
    Kent Beck

    View Slide

  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?

    View Slide

  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?

    View Slide

  127. @PreusslerBerlin
    Remember!
    • Never write new functionality without test first
    • Tests need to be refactored and clean!

    View Slide

  128. @PreusslerBerlin
    TDD on Android?
    • its possible!
    • its fun!
    https://www.flickr.com/photos/chefranden/14838138493

    View Slide

  129. @PreusslerBerlin
    TDD on Android?
    Unit tests (even with TDD)
    are NOT the only tests you need

    View Slide

  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

    View Slide

  131. @PreusslerBerlin
    Tools used
    • Protected onCreate call in Kotlin:
    github.com/
    dpreussler/android-tdd-utils
    • Test friendly dependency Injection:
    github.com/
    stephanenicolas/toothpick

    View Slide

  132. @PreusslerBerlin
    Tools used
    • Some test utils code for FragmentActivity,
    check TestUtils.kt
    github.com/
    sporttotal-tv/android-tdd-workshop

    View Slide

  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/

    View Slide

  134. Testdrive your
    android app
    @PreusslerBerlin

    View Slide