We have all heard about TDD: Test Driven Development. It produces better code and leads to fewer bugs. But, in our daily Android lives, why aren’t we doing it? Is it possible to develop an Android app that is fully test driven? Where do you start? Should you only test Java classes? Should you use or avoid Robolectric?
9. @PreusslerBerlin
History
• Started with test first from XP in 1999 by Kent
Beck
• Just a rediscovery:
test first was normal in early days of
programming
• Famous as TDD since Kent Becks book in 2003
10. @PreusslerBerlin
History
• Started with test first from XP in 1999 by Kent
Beck
• Just a rediscovery:
test first was normal in early days of
programming
• Famous as TDD since Kent Becks book in 2003
11. @PreusslerBerlin
History
• Started with test first from XP in 1999 by Kent
Beck
• Just a rediscovery:
test first was normal in early days of
programming
• Famous as TDD since Kent Becks book in 2003
13. @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)
14. @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)
15. @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)
17. @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)
18. @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)
19. @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)
25. @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
26. @PreusslerBerlin
Why?
•YAGNI and KISS out of the box
•No more over engineering
•Test all business needs
•Eliminate debugging
•Minimize use of Android devices
48. @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
49. @PreusslerBerlin
Isn’t it slow?
With TDD, you spend a year writing code which
actually works. Then you do final integration
testing for a few weeks.
50. @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
59. @PreusslerBerlin
val binding = mock<ActivityPrivacyPolicyBinding>()
val tested = PrivacyPolicyActivity()
@Nested
inner class `When created` {
@Test
fun `sets viewmodel`() {
tested.onCreate(null)
verify(binding).viewModel =
navigationViewModel
}
}
Does this work?
60. @PreusslerBerlin
Android SDK under test
• Android classes can be loaded on JVM:
build/generated/mockable-android-XX.jar
• Removes finals and ALL the code!
61. @PreusslerBerlin
Android SDK under test
• Empty methods with default return values
android {
…
testOptions {
unitTests.returnDefaultValues = true
}
-> no code will run
62. @PreusslerBerlin
The problem of v4… Activity
• mockable.jar not existing for libraries,
including support library L
-> real code will run
63. @PreusslerBerlin
The problem of v4… Activity
• Helper method
fun <T : FragmentActivity> T.prepareForTest(): T {
…
whenever(supportFragmentManager).thenReturn(mockSupportFragmentManager)
whenever(fragmentManager).thenReturn(mockFragmentManager)
whenever(layoutInflater).thenReturn(mockLayoutInflater)
}
64. @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
}
}
65. @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
}
}
66. @PreusslerBerlin
@Nested
inner class `When started` {
@Test
fun `forwards lifecylce to viewmodel`() {
tested.onStart()
verify(viewModel).onStart() }
}
java.lang.NullPointerException
at
android.support.v17.leanback.app.PlaybackSupportFragment.setupChild
FragmentLayout(PlaybackSupportFragment.java:730)
at
android.support.v17.leanback.app.PlaybackSupportFragment.onStart(Pl
aybackSupportFragment.java:899)
69. @PreusslerBerlin
Avoid activity and fragment code
• Use life cycle aware components!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
observeLifecycleIn(viewModel)
}
@OnLifecycleEvent(ON_START)
fun onStart() {
….
}
70. @PreusslerBerlin
Avoid activity and fragment code
• Testing android view classes are hard
• So let’s make sure we don’t need to test them!
Use the power of data binding
83. @PreusslerBerlin
Tired of issues like
java.lang.NullPointerException
at
org.robolectric.manifest.MetaData
.init(MetaData.java:55)
at
org.robolectric.manifest.AndroidM
anifest.initMetaData(AndroidManif
est.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?
87. @PreusslerBerlin
Robolectric 4
• Robolectric is just another “android device”
when using Espresso like API
• Not compatible with Junit5
• More stable now as of new API?!
• Still slower!?
92. @PreusslerBerlin
What about 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
93. @PreusslerBerlin
What about 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
94. @PreusslerBerlin
What about 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
95. @PreusslerBerlin
What about 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
110. @PreusslerBerlin
TDD and tests
• Kent Beck spoke about behavior of the system
in his TDD book
• A “unit” does not mean every class/method!
Unit came from Blackbox!
• TDD tests behaviors not implementation details!
• What your software does, is stable!
How it does this, is unstable!
TDD what went wrong: https://www.youtube.com/watch?v=EZ05e7EMOLM
111. @PreusslerBerlin
TDD and tests
• Kent Beck spoke about behavior of the system
in his TDD book
• A “unit” does not mean every class/method!
Unit came from Blackbox!
• TDD tests behaviors not implementation details!
• What your software does, is stable!
How it does this, is unstable!
TDD what went wrong: https://www.youtube.com/watch?v=EZ05e7EMOLM
112. @PreusslerBerlin
TDD and tests
• Kent Beck spoke about behavior of the system
in his TDD book
• A “unit” does not mean every class/method!
Unit came from Blackbox!
• TDD tests behaviors not implementation details!
• What your software does, is stable!
How it does this, is unstable!
TDD what went wrong: https://www.youtube.com/watch?v=EZ05e7EMOLM
113. @PreusslerBerlin
TDD and tests
• Kent Beck spoke about behavior of the system
in his TDD book
• A “unit” does not mean every class/method!
Unit came from Blackbox!
• TDD tests behaviors not implementation details!
• What your software does, is stable!
How it does this, is unstable!
TDD what went wrong: https://www.youtube.com/watch?v=EZ05e7EMOLM
119. @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?
120. @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?
121. @PreusslerBerlin
TDD on Android?
• Its possible!
• Might feel extreme
• But it’s fun!
https://www.flickr.com/photos/chefranden/14838138493
122. More resources
• Test Driven Development by Example (Kent Beck)
• TDD what went wrong:
https://www.youtube.com/watch?v=EZ05e7EMOLM
• The three laws of TDD by Uncle Bob
https://www.youtube.com/watch?time_continue=1
7&v=AoIfc5NwRks
• https://cleancoders.com/videos