TDD on Android

TDD on Android

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

A8b79d304b5184e5a5b0a109590f6683?s=128

Danny Preussler

June 25, 2018
Tweet

Transcript

  1. TDD on Android Danny Preussler Head of Mobile

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

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

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

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

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

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

    (with your code) • Test code will become more important than your production code
  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)
  12. @PreusslerBerlin

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

  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)
  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 Red Green Refactor • Make it fail • Make

    it work • Make it right micro-cycle (minutes)
  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)
  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 WTF

  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.
  36. @PreusslerBerlin Red Green Refactor • Always be one step away

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

    from green bar • Think of new test-> write it down
  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
  39. @PreusslerBerlin Code is your ToDo list class PanoramaTrackerTest { @Test

    fun `should track button click`() { fail ("Danny forgot me") } }
  40. @PreusslerBerlin Why? • YAGNI and KISS out of the box

    • Test all business needs • Eliminate debugging
  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
  42. @PreusslerBerlin Start somewhere

  43. @PreusslerBerlin Start somewhere

  44. @PreusslerBerlin Start somewhere

  45. @PreusslerBerlin Start somewhere

  46. @PreusslerBerlin Start somewhere

  47. @PreusslerBerlin Start somewhere

  48. @PreusslerBerlin Start somewhere

  49. @PreusslerBerlin Start somewhere

  50. @PreusslerBerlin Start somewhere

  51. @PreusslerBerlin Start somewhere

  52. @PreusslerBerlin Start somewhere

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

  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
  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
  56. @PreusslerBerlin Isn’t it slow? Research shows that TDD: adds 10%—30%

    on initial costs = longer to complete their projects
  57. @PreusslerBerlin Isn’t it slow? Research shows that TDD: • Reduces

    defect density by 60-90 % • Reduces production bug density by 40–80%
  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.
  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
  60. @PreusslerBerlin TDD and architecture

  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
  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
  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
  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
  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
  66. @PreusslerBerlin Android

  67. @PreusslerBerlin Android SDK under test • Android classes can be

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

    return values android { … testOptions { unitTests.returnDefaultValues = true } -> no code will run
  69. @PreusslerBerlin The problem of v4… Activity • mockable.jar not existing

    for libraries, including support library L -> real code will run
  70. @PreusslerBerlin Solutions for v4… Activity • Robolectric • Helper method

    fun <T : FragmentActivity> T.prepareForTest(): T { … whenever(supportFragmentManager).thenReturn(mockSupportFragmentManager) whenever(fragmentManager).thenReturn(mockFragmentManager) whenever(layoutInflater).thenReturn(mockLayoutInflater) }
  71. @PreusslerBerlin val binding = mock<ActivityPrivacyPolicyBinding>() val tested = PrivacyPolicyActivity().prepareForTest( @Nested

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

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

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

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

    inner class `When created` { @Test fun `sets viewmodel`() { tested.onCreate(null) verify(binding).viewModel = navigationViewModel } } protected
  76. @PreusslerBerlin package android.app fun Activity.onCreate(bundle: Bundle?) = this.onCreate(bundle) github.com/ dpreussler/android-tdd-utils

  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!
  78. @PreusslerBerlin Data binding… XML ViewModel bind

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

    one big android question once and for all
  80. @PreusslerBerlin What’s left? • Activities needs to be declared in

    manifest • Permissions for older devices (internet)
  81. @PreusslerBerlin What’s left? • Manifest via lint lintOptions { check

    'Registered' warningsAsErrors true } * Has issues with Gradle Plugin 3.0 and Kotlin
  82. @PreusslerBerlin What’s left?

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

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

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

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

    fast! • need to run continuously • fast feedback
  87. @PreusslerBerlin What about Robolectric?

  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?
  89. @PreusslerBerlin Limitations • Custom view and animation code might get

    too hard
  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
  91. @PreusslerBerlin "Test only if you would want it to work.”

    Kent Beck
  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?
  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?
  94. @PreusslerBerlin Remember! • Never write new functionality without test first

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

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

    NOT the only tests you need
  97. @PreusslerBerlin Tools used • Protected onCreate call in Kotlin: github.com/

    dpreussler/android-tdd-utils
  98. @PreusslerBerlin Tools used • Some test utils code for FragmentActivity,

    check TestUtils.kt github.com/ sporttotal-tv/android-tdd-workshop
  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/
  100. TDD on Android @PreusslerBerlin