Test Driven Development (on Android) Kotlin Kenya Danny Preussler @PreusslerBerlin
about:me • Finished university in 2001 • Switched to Java in 2003 • Switched to Kotlin in 2016 • Worked for eBay, Groupon, Viacom • Android Engineer at SoundCloud • GDE since 2016
about:sc • Founded 2007 in Berlin, Germany • World’s largest open audio platform • 200 million tracks, 25 million creators (2019) • Around 500 employees • Playstore: 100,000,000+ installs • Ratings: 4.7 (5,500,500) Our Mission is to lead what’s next in music. Our Purpose is to accelerate creators' careers.
TDD Brotherhood
TDD Brotherhood We swear that: We will not write a single line of code before writing a test
TDD Brotherhood ● Extreme Programming, Kent Beck 1999 ● TDD by Example, Kent Becks, 2003
TDD Brotherhood ● Just a rediscovery ● test first was normal in early days of programming
● Software Crafters ● Pair / Mob programming ● Trunk based development ● TDD ● Practise their craft with Katas The real TDD Brotherhood
“Code without tests is bad code.” (Michael C. Feathers)
“any code without test is a legacy code.” (Michael C. Feathers)
“how do you know something works when you don’t have test for it?” (Robert ‘Uncle Bob’ Martin)
“Refactoring without good test coverage is changing shit” (Martin Fowler)
TDD is not about testing
The 3 rules of TDD •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)
The 3 rules of TDD •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)
The 3 rules of TDD •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)
The 3 rules of TDD •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)
The 3 rules of TDD •Create a unit tests that fails •Write just enough production code to makes that test pass. •Clean up the mess you just made. •REPEAT micro-cycle (minutes)
Red Green Refactor •Make it fail •Make it work •Make it right micro-cycle (minutes)
Be precise: •You must not write more of a test than is sufficient to fail •Not compiling is failing! •You must not write more production code than is sufficient to make the currently failing test pass micro-cycle (minutes)
Be precise: •You must not write more of a test than is sufficient to fail •Not compiling is failing! •You must not write more production code than is sufficient to make the currently failing test pass. micro-cycle (minutes)
Be precise: •You must not write more of a test than is sufficient to fail •Not compiling is failing! •You must not write more production code than is sufficient to make the currently failing test pass. micro-cycle (minutes)
WTF
Baby steps Based: our limited minds are not capable of two simultaneous goals: 1. Correct behavior. 2. Correct structure. https://www.flickr.com/photos/21561428@N03/4616816371
Red Green Refactor •You need red: • as no one tests the tests
Red Green Refactor •In green: • Speed trumps design! • Make it dirty!
Red Green Refactor • One step at a time = Don’t get distracted!
Red Green Refactor • One step at a time = Don’t get distracted! • Think of new test -> write it down
Red Green Refactor • One step at a time = Don’t get distracted! • It’s getting ugly? -> write it down
Let’s code
Start somewhere
Write the tests that forces you to write the code you want to write
Write the tests that forces you to write the code you want to write Tip: Create a list of tests on paper
Tests tell a story
Tests tell a story
Tests tell a story • Test your behaviour • Think about your domain
What to mock?
What about mocking
• Tests are meant for refactoring • Mocks often break on refactoring What about mocking
interface TrackRepository { suspend fun track(track: Urn): Track suspend fun tracks(tracks: List<Urn>): List<Track> What about mocking ● Mocks hardode implementation details
What about mocking • TDD changes the way what you test: Test1 Test2 Class1 Class2 Class3 Test3
What about mocking •TDD changes the way what you test: Test1 Class1 Class2 Class3
What about mocking •TDD changes the way what you test: Test1 Class1
What about mocking •TDD changes the way what you test: Test1 Class1 Class2 Class3
What about mocking •Tests will become more solid Test1 ViewModel Repository API
What about mocking •Tests will become more solid Test1 ViewModel Repository API Store Reducer SideEffects
What about mocking •Tests will become more solid Test1 ViewModel Repository API Store Reducer SideEffects
What about mocking •Mock your outside dependency (modules) Test1 ViewModel Repository API Store Reducer SideEffects
That’s not a unit test!
That’s not a unit test!
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
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
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
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
TDD and tests • unit test must be fast! • no databases • no network calls • if -> integration test martinfowler.com
TDD on Android
Android SDK under test • This used to do the trick: android { … testOptions { unitTests.returnDefaultValues = true }
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) Try to stay away from Activities/Fragments
Android SDK under test •Wrap things class BundleBuilder @Inject constructor() { operator fun invoke() = Bundle() }
Android SDK under test •Wrap things class BundleBuilder @Inject constructor() { operator fun invoke() = Bundle() operator fun invoke(vararg pairs: Pair<String, Any?>) = bundleOf(*pairs) }
Android SDK under test •Wrap things (keep it simple) @VisibleForTesting var bundleBuilder: () -> Bundle = { Bundle() }
ViewModels encapsualate view state testable
DATA BINDING!
Data binding… XML ViewModel bind
Compose Composable ViewModel bind
What about Robolectric?
Robolectric 4 •Android builds are already slow! •Robolectric is just another “android device” •Not compatible with Junit5 •Still slower than pure junit •It’s goal: replace Espresso not JVM Tests
be pragmatic, not dogmatic @RobolectricNeededAsOf(Uri::class) @RunWith(RobolectricTestRunner::class) class UploadStarterTest {
What’s left?
Whats left? • UI: Jetpack Compose!
What’s left? •Activities needs to be declared in manifest •Permissions for older devices
What’s left?
What’s left? • Manifest via lint lintOptions { check 'Registered' warningsAsErrors true }
What’s left? • Zero tolerance policy very valuable
What’s left? • Get access to merged manifest and resources: testOptions { unitTests.includeAndroidResources = true } com/android/tools/ test_config.properties
Code coverage?
What about code coverage In TDD 100% coverage is a side effect not a goal!
Is’nt that slow?
Isn’t it slow? Writing tests is slower than not writing tests. You’ll write at least as much test code as production code
Isn’t it slow? Writing tests is slower than not writing tests. You’ll write at least as much test code as production code
Isn’t it slow? Research shows that TDD: adds 10%—30% on initial costs = longer to complete their projects
Isn’t it slow? Research shows that TDD: •Reduces defect density by 60-90 % •Reduces production bug density by 40–80%
Without TDD, you spend a few weeks writing code which mostly works and spend the next year(s) "testing" and fixing many (but not all) of the bugs
With TDD, you spend a year writing code which actually works.
Isn’t it slow? •Single + Initial feature will take longer •Bugfixing phase is shorter •Debugging disappears •Ci finds bugs before QA does • Long term it’s much faster no more big rewrite
Isn’t it slow? •Single + Initial feature will take longer •Bugfixing phase is shorter •Debugging disappears •Ci finds bugs before QA does • Long term it’s much faster no more big rewrite
Isn’t it slow? •Single + Initial feature will take longer •Bugfixing phase is shorter •Debugging disappears •Ci finds bugs before QA does • Long term it’s much faster no more big rewrite
Isn’t it slow? •Single + Initial feature will take longer •Bugfixing phase is shorter •Debugging disappears •Ci finds bugs before QA does • Long term it’s much faster no more big rewrite
Isn’t it slow? •Single + Initial feature will take longer •Bugfixing phase is shorter •Debugging disappears •Ci finds bugs before QA does • Long term it’s much faster no more big rewrite
Benefits of TDD
Benefits •YAGNI and KISS out of the box •No more over-engineering •Test all business needs •Minimize debugging •Minimize use of Android devices
Benefits •YAGNI and KISS out of the box •No more over-engineering •Test all business needs •Minimize debugging •Minimize use of Android devices
Benefits •YAGNI and KISS out of the box •No more over-engineering •Test all business needs •Minimize debugging •Minimize use of Android devices
Benefits •YAGNI and KISS out of the box •No more over-engineering •Test all business needs •Minimize debugging •Minimize use of Android devices
Benefits •YAGNI and KISS out of the box •No more over-engineering •Test all business needs •Minimize debugging •Minimize use of Android devices
Benefits •It’s Gamification -> fun •Small Changes -> Small Pull requests •Always know what’s next
Benefits •It’s Gamification -> fun •Small Changes -> Small Pull requests •Always know what’s next
Benefits •It’s Gamification -> fun •Small Changes -> Small Pull requests •Always know what’s next
Benefits •Less stressful •Interrupt any time •Go home any time •No need for “flow”
“Never ask permission to refactor. Never ask permission to write tests. You do these things because you KNOW they are the best way to go fast.“ Robert C Martin
Mark Seemann (https://blog.ploeh.dk/2019/03/18/the-programmer-as-decision-maker/)
Grace Hopper "It's easier to ask forgiveness than it is to get permission."
"Test only if you would want it to work.” Kent Beck
If it's worth building, it's worth testing If it's not worth testing, why are you wasting your time working on it?
If it's worth building, it's worth testing If it's not worth testing, why are you wasting your time working on it?
TDD on Android? • Its possible! • Might feel extreme • But it’s fun! https://www.flickr.com/photos/chefranden/14838138493
More resources
More resources • TDD what went wrong: https://www.youtube.com/watch?v=EZ05e7EMOLM • The three laws of TDD by Uncle Bob https://www.youtube.com/watch?v=AoIfc5NwRks • TDD for those who don't need it https://www.youtube.com/watch?v=a6oP24CSdUg
More resources • https://online-training.jbrains.ca/p/wbitdd-01 • https://medium.com/androiddevelopers/write-once-run- everywhere-tests-on-android-88adb2ba20c5 • github.com/sporttotal-tv/android-tdd-workshop
Join the Brotherhood Danny Preussler @PreusslerBerlin

Test Driven Development on Android (Kotlin Kenya)