Share
Embed
TDD on Android
Test driven development: Whats the reason? Whats the impact? What are the issues on Android?
Danny Preussler
June 25, 2018
Transcript
100 slides
-
@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 -
@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 -
@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 -
@PreusslerBerlin “Code without tests is bad code.” “any code without
test is a legacy code.” (Michael C. Feathers) -
@PreusslerBerlin “Code without tests is bad code.” “any code without
test is legacy code.” (Michael C. Feathers) -
@PreusslerBerlin Tests give confidence “how do you know something works
when you don’t have test for it?” (Robert ‘Uncle Bob’ Martin) -
@PreusslerBerlin Tests allow refactoring “Refactoring without good test coverage is
changing shit” (Martin Fowler) -
@PreusslerBerlin Tests are documentation • The only doc that lives
(with your code) • Test code will become more important than your production code -
@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) -
@PreusslerBerlin What I learned • ”test after” does not work
(enough) Class1 Class2 Class3 -
@PreusslerBerlin What I learned • ”test after” does not work
(enough) Test1 Test2 Class1 Class2 Class3 Test3 -
@PreusslerBerlin What I learned • ”test after” does not work
(enough) Test1 Class1 Class2 Class3 -
@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. -
@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. -
@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. -
@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. -
@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. -
@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. -
@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. -
@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. -
@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. -
@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) -
@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) -
@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) -
@PreusslerBerlin Red Green Refactor • Make it fail • Make
it work • Make it right micro-cycle (minutes) -
@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) -
@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) -
@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) -
@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. -
@PreusslerBerlin Red Green Refactor • Always be one step away
from green bar • Think of new test-> write it down -
@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 -
@PreusslerBerlin Code is your ToDo list class PanoramaTrackerTest { @Test
fun `should track button click`() { fail ("Danny forgot me") } } -
@PreusslerBerlin Why? • YAGNI and KISS out of the box
• Test all business needs • Eliminate debugging -
@PreusslerBerlin My experience • Less coupled to implementation • Less
mocking • Forces small changes • Interruptions from colleagues always possible • No need for a ”flow” • Less stress -
@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 -
@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 -
@PreusslerBerlin Isn’t it slow? Research shows that TDD: adds 10%—30%
on initial costs = longer to complete their projects -
@PreusslerBerlin Isn’t it slow? Research shows that TDD: • Reduces
defect density by 60-90 % • Reduces production bug density by 40–80% -
@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. -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@PreusslerBerlin Android SDK under test • Android classes can be
loaded on JVM: build/generated/mockable-android-XX.jar • No more finals! -
@PreusslerBerlin Android SDK under test • Empty methods with default
return values android { … testOptions { unitTests.returnDefaultValues = true } -> no code will run -
@PreusslerBerlin The problem of v4… Activity • mockable.jar not existing
for libraries, including support library L -> real code will run -
@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) } -
@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 } } -
@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 } } -
@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 -
@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 -
@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 -
@PreusslerBerlin The power of data binding • Testing android view
classes are hard • So let’s make sure we don’t need to test them! -
@PreusslerBerlin Data binding… who is the view? • Solves the
one big android question once and for all -
@PreusslerBerlin What’s left? • Activities needs to be declared in
manifest • Permissions for older devices (internet) -
@PreusslerBerlin What’s left? • Manifest via lint lintOptions { check
'Registered' warningsAsErrors true } * Has issues with Gradle Plugin 3.0 and Kotlin -
@PreusslerBerlin What about Espresso • problem: • need to be
fast! • need to run continuously • fast feedback -
@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? -
@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 -
@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? -
@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? -
@PreusslerBerlin Remember! • Never write new functionality without test first
• Tests need to be refactored and clean! -
@PreusslerBerlin TDD on Android? • its possible! • its fun!
https://www.flickr.com/photos/chefranden/14838138493 -
@PreusslerBerlin TDD on Android? Unit tests (even with TDD) are
NOT the only tests you need -
@PreusslerBerlin Tools used • Protected onCreate call in Kotlin: github.com/
dpreussler/android-tdd-utils -
@PreusslerBerlin Tools used • Some test utils code for FragmentActivity,
check TestUtils.kt github.com/ sporttotal-tv/android-tdd-workshop -
@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/