Share
Embed
TDD on Android (MobOS 2018)
Slides for my workshop giving in Cluj
Source: https://github.com/sporttotal-tv/android-tdd-workshop
Danny Preussler
February 16, 2018
Transcript
134 slides
-
@PreusslerBerlin The source • Please download github.com/ sporttotal-tv/android-tdd-workshop • Branch
master • Run FirstTest.test() @PreusslerBerlin -
@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 work
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 About me • Talking about testing for 10y now
• Its all about discipline • I am a learner like you • Let’s dive in together -
@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 “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 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 • It’s getting ugly? -> write it down -
@PreusslerBerlin Why? • YAGNI and KISS out of the box
• eliminate debugging -
@PreusslerBerlin Why? • Test all business needs • Less coupled
to implementation • Less mocking • Forces small changes • TDD is more about design -
@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 TDD adds 10% — 30% on initial costs = longer to complete their projects -
@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 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? • Feature takes longer • You
write at least as much test code as production code -
@PreusslerBerlin Isn’t it slow? • 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 Violations of DRY are typically referred to as WET
solutions, which is commonly taken to stand for either "write everything twice" or "we enjoy typing” (http://en.wikipedia.org/wiki/Don't_repeat_yourself) -
@PreusslerBerlin Lets talk about Activities • now the tricky part
starts https://www.flickr.com/photos/muchadoaboutnothing/438734626 -
@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 Dependency Inversion Principle High level entities should not
depend on low level details. -
@PreusslerBerlin Inversion of Control Who initiates a message Hollywood's Law:
don't call me, I'll call you. -
@PreusslerBerlin Inversion of Control Common implementations: • Factory • Service
Locator • Dependency Injection -
@PreusslerBerlin Inversion of Control Common implementations: • Factory tracker =
Factory.createTracker() • Service Locator • Dependency Injection -
@PreusslerBerlin Inversion of Control Common implementations: • Factory • Service
Locator tracker = Locator.get(Tracker.class) • Dependency Injection -
@PreusslerBerlin Inversion of Control Common implementations: • Factory • Service
Locator • Dependency Injection @Inject Tracker tracker; -
@PreusslerBerlin Rotation • Many different solutions • Let’s go with
view model from Architecture Components -
@PreusslerBerlin Rotation • Many different solutions • Let’s go with
view model from Architecture Components -
@PreusslerBerlin life cycle: rotation onCreate onStart onResume onPause onStop onDestroy
onCreate onStart onResume ViewModel -
@PreusslerBerlin The problem of v4… Activity • mockable.jar not existing
for libraries, including support library L -> real code will run -
@PreusslerBerlin What’s left? • Activities needs to be declared in
manifest • Permissions for older devices (internet) -
@PreusslerBerlin Live coding • Manifest via lint lintOptions { check
'Registered' warningsAsErrors true } * Has issues with Gradle Plugin 3.0 and Kotlin @PreusslerBerlin -
@PreusslerBerlin Lets talk about 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 ViewModel in Data Binding <TextView … android:id="@+id/all_shows_item_title" android:text="@{viewModel.title}" />
<data> <variable name="viewModel" type=”tv.sporttotal. ViewModel"/> </data> -
@PreusslerBerlin .. show a toast class SeriesViewModel : Viewmodel() {
… @Bindable var error = ObservableField<String>() -
@PreusslerBerlin .. show a toast viewModel.error.addOnPropertyChangedCallback( object : OnPropertyChangedCallback() {
override fun onPropertyChanged(…) { showToast(viewModel.error.get() } }) -
@PreusslerBerlin .. show a toast (alternative) <FrameLayout app:showError="@{viewModel.errorAppeared}"> @BindingAdapter("showError") fun
ViewGroup.onErrorAppeared(error: String?){ errorString?.let { showToast(context, error)) } } -
@PreusslerBerlin Data binding full picture XML Activity ViewModel (un)bind bind
Life cycle aware class (un)bind -
@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 -
@PreusslerBerlin What’s wrong with Robolectric? • Developers used too much
magic forgot what unit test should be • Your tests rely on correct 3rd party implementation • Tests itself becomes flaky • It’s slower than pure JUnit -
@PreusslerBerlin Android today • Projects not designed to be testable
have often need for Robolectric -
@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 • github.com/MarkusAmshove/Kluent • github.com/nhaarman/mockito-kotlin • Final mocking
with mockito2: github.com/mockito/mockito/wiki/What%2 7s-new-in-Mockito-2#mock-the- unmockable-opt-in-mocking-of-final- classesmethods -
@PreusslerBerlin Tools used • Protected onCreate call in Kotlin: github.com/
dpreussler/android-tdd-utils • Test friendly dependency Injection: github.com/ stephanenicolas/toothpick -
@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/