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

TDD on Android

TDD on Android

Test driven development: Whats the reason? Whats the impact? What are the issues on Android?

Danny Preussler

June 25, 2018
Tweet

More Decks by Danny Preussler

Other Decks in Programming

Transcript

  1. TDD
    on Android
    Danny Preussler
    Head of Mobile

    View Slide

  2. @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

  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
    Let’s
    talk about
    tests

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. @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

  12. @PreusslerBerlin

    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
    How does
    it work?

    View Slide

  27. @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

  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
    Red Green Refactor
    • Make it fail
    • Make it work
    • Make it right
    micro-cycle (minutes)

    View Slide

  31. @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

  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
    WTF

    View Slide

  35. @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

  36. @PreusslerBerlin
    Red Green Refactor
    • Always be one step away from green bar

    View Slide

  37. @PreusslerBerlin
    Red Green Refactor
    • Always be one step away from green bar
    • Think of new test-> write it down

    View Slide

  38. @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

  39. @PreusslerBerlin
    Code is your ToDo list
    class PanoramaTrackerTest {
    @Test
    fun `should track button click`() {
    fail ("Danny forgot me")
    }
    }

    View Slide

  40. @PreusslerBerlin
    Why?
    • YAGNI and KISS out of the box
    • Test all business needs
    • Eliminate debugging

    View Slide

  41. @PreusslerBerlin
    My experience
    • Less coupled to implementation
    • Less mocking
    • Forces small changes
    • Interruptions from colleagues always possible
    • No need for a ”flow”
    • Less stress

    View Slide

  42. @PreusslerBerlin
    Start somewhere

    View Slide

  43. @PreusslerBerlin
    Start somewhere

    View Slide

  44. @PreusslerBerlin
    Start somewhere

    View Slide

  45. @PreusslerBerlin
    Start somewhere

    View Slide

  46. @PreusslerBerlin
    Start somewhere

    View Slide

  47. @PreusslerBerlin
    Start somewhere

    View Slide

  48. @PreusslerBerlin
    Start somewhere

    View Slide

  49. @PreusslerBerlin
    Start somewhere

    View Slide

  50. @PreusslerBerlin
    Start somewhere

    View Slide

  51. @PreusslerBerlin
    Start somewhere

    View Slide

  52. @PreusslerBerlin
    Start somewhere

    View Slide

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

    View Slide

  54. @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

    View Slide

  55. @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

    View Slide

  56. @PreusslerBerlin
    Isn’t it slow?
    Research shows that TDD:
    adds 10%—30% on initial costs
    = longer to complete their projects

    View Slide

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

    View Slide

  58. @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

  59. @PreusslerBerlin
    Isn’t it slow?
    • Single feature will take longer
    • Bugfixing phase is shorter
    • Debugging disappears
    • Ci finds bugs before tester does
    • Long term it’s much faster no more big rewrite

    View Slide

  60. @PreusslerBerlin
    TDD and
    architecture

    View Slide

  61. @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

  62. @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

  63. @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

  64. @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

  65. @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

  66. @PreusslerBerlin
    Android

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

  70. @PreusslerBerlin
    Solutions for v4… Activity
    • Robolectric
    • Helper method
    fun T.prepareForTest(): T {

    whenever(supportFragmentManager).thenReturn(mockSupportFragmentManager)
    whenever(fragmentManager).thenReturn(mockFragmentManager)
    whenever(layoutInflater).thenReturn(mockLayoutInflater)
    }

    View Slide

  71. @PreusslerBerlin
    val binding = mock()
    val tested = PrivacyPolicyActivity().prepareForTest(
    @Nested
    inner class `When created` {
    @Test
    fun `sets viewmodel`() {
    tested.onCreate(null)
    verify(binding).viewModel =
    navigationViewModel
    }
    }

    View Slide

  72. @PreusslerBerlin
    val binding = mock()
    val tested = PrivacyPolicyActivity().prepareForTest(
    @Nested
    inner class `When created` {
    @Test
    fun `sets viewmodel`() {
    tested.onCreate(null)
    verify(binding).viewModel =
    navigationViewModel
    }
    }

    View Slide

  73. @PreusslerBerlin
    val binding = mock()
    val tested = PrivacyPolicyActivity().prepareForTest(
    @Nested
    inner class `When created` {
    @Test
    fun `sets viewmodel`() {
    tested.onCreate(null)
    verify(binding).viewModel =
    navigationViewModel
    }
    }
    protected

    View Slide

  74. @PreusslerBerlin
    val binding = mock()
    val tested = PrivacyPolicyActivity().prepareForTest(
    @Nested
    inner class `When created` {
    @Test
    fun `sets viewmodel`() {
    tested.onCreate(null)
    verify(binding).viewModel =
    navigationViewModel
    }
    }
    protected

    View Slide

  75. @PreusslerBerlin
    val binding = mock()
    val tested = PrivacyPolicyActivity().prepareForTest(
    @Nested
    inner class `When created` {
    @Test
    fun `sets viewmodel`() {
    tested.onCreate(null)
    verify(binding).viewModel =
    navigationViewModel
    }
    }
    protected

    View Slide

  76. @PreusslerBerlin
    package android.app
    fun Activity.onCreate(bundle: Bundle?)
    = this.onCreate(bundle)
    github.com/
    dpreussler/android-tdd-utils

    View Slide

  77. @PreusslerBerlin
    The power of data binding
    • Testing android view classes are hard
    • So let’s make sure we don’t need to test them!

    View Slide

  78. @PreusslerBerlin
    Data binding…
    XML ViewModel
    bind

    View Slide

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

    View Slide

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

    View Slide

  81. @PreusslerBerlin
    What’s left?
    • Manifest via lint
    lintOptions {
    check 'Registered'
    warningsAsErrors true
    }
    * Has issues with Gradle Plugin 3.0 and Kotlin

    View Slide

  82. @PreusslerBerlin
    What’s left?

    View Slide

  83. @PreusslerBerlin
    What’s left?
    • Zero tolerance policy very valuable

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  87. @PreusslerBerlin
    What about
    Robolectric?

    View Slide

  88. @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
    Still valid?

    View Slide

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

    View Slide

  90. @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

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

    View Slide

  92. @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

  93. @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

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

    View Slide

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

    View Slide

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

    View Slide

  97. @PreusslerBerlin
    Tools used
    • Protected onCreate call in Kotlin:
    github.com/
    dpreussler/android-tdd-utils

    View Slide

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

    View Slide

  99. @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

  100. TDD
    on Android
    @PreusslerBerlin

    View Slide