Testdrive your
android app
Danny Preussler
Head of Mobile
Slide 2
Slide 2 text
@PreusslerBerlin
The source
• Please download
github.com/
sporttotal-tv/android-tdd-workshop
• Branch
master
• Run
FirstTest.test()
@PreusslerBerlin
Slide 3
Slide 3 text
@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
Slide 4
Slide 4 text
@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
Slide 5
Slide 5 text
@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
Slide 6
Slide 6 text
@PreusslerBerlin
Let’s
talk about
tests
Slide 7
Slide 7 text
@PreusslerBerlin
“Code without tests is bad code.”
“any code without test is a legacy code.”
(Michael C. Feathers)
Slide 8
Slide 8 text
@PreusslerBerlin
“Code without tests is bad code.”
“any code without test is legacy code.”
(Michael C. Feathers)
Slide 9
Slide 9 text
@PreusslerBerlin
Tests give confidence
“how do you know something work when you
don’t have test for it?”
(Robert ‘Uncle Bob’ Martin)
Slide 10
Slide 10 text
@PreusslerBerlin
Tests allow refactoring
“Refactoring without good test coverage
is changing shit”
(Martin Fowler)
Slide 11
Slide 11 text
@PreusslerBerlin
Tests are documentation
• The only doc that lives (with your code)
• Test code will become more important than
your production code
Slide 12
Slide 12 text
@PreusslerBerlin
About me
• Talking about testing for 10y now
• Its all about discipline
• I am a learner like you
• Let’s dive in together
Slide 13
Slide 13 text
@PreusslerBerlin
What I learned
• ”test after” does not work (enough)
Class1 Class2 Class3
Slide 14
Slide 14 text
@PreusslerBerlin
What I learned
• ”test after” does not work (enough)
Test1 Test2
Class1 Class2 Class3
Test3
Slide 15
Slide 15 text
@PreusslerBerlin
What I learned
• ”test after” does not work (enough)
Test1
Class1 Class2 Class3
Slide 16
Slide 16 text
@PreusslerBerlin
Look
outside
the box
Slide 17
Slide 17 text
@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.
Slide 18
Slide 18 text
@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.
Slide 19
Slide 19 text
@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.
Slide 20
Slide 20 text
@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.
Slide 21
Slide 21 text
@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.
Slide 22
Slide 22 text
@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.
Slide 23
Slide 23 text
@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.
Slide 24
Slide 24 text
@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.
Slide 25
Slide 25 text
@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.
Slide 26
Slide 26 text
@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)
Slide 27
Slide 27 text
@PreusslerBerlin
How does
it work?
Slide 28
Slide 28 text
@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)
Slide 29
Slide 29 text
@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)
Slide 30
Slide 30 text
@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)
Slide 31
Slide 31 text
@PreusslerBerlin
Red Green Refactor
• Make it fail
• Make it work
• Make it right
micro-cycle (minutes)
Slide 32
Slide 32 text
@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)
Slide 33
Slide 33 text
@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)
Slide 34
Slide 34 text
@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)
Slide 35
Slide 35 text
@PreusslerBerlin
WTF
Slide 36
Slide 36 text
@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.
Slide 37
Slide 37 text
@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
Slide 38
Slide 38 text
@PreusslerBerlin
Why?
• YAGNI and KISS out of the box
• eliminate debugging
Slide 39
Slide 39 text
@PreusslerBerlin
Why?
• Test all business needs
• Less coupled to implementation
• Less mocking
• Forces small changes
• TDD is more about design
Slide 40
Slide 40 text
@PreusslerBerlin
Isn’t that
slow?
https://pixabay.com/en/snail-illustration-drawing-yellow-1757756/
Slide 41
Slide 41 text
@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
Slide 42
Slide 42 text
@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
Slide 43
Slide 43 text
@PreusslerBerlin
Isn’t it slow?
Research shows that TDD:
• Reduces defect density by 60-90 %
• Reduces production bug density by 40–80%
Slide 44
Slide 44 text
@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.
Slide 45
Slide 45 text
@PreusslerBerlin
Isn’t it slow?
• Feature takes longer
• You write at least as much test code as
production code
Slide 46
Slide 46 text
@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
Live coding
• The countdown
@PreusslerBerlin
Slide 49
Slide 49 text
@PreusslerBerlin
Live coding
• Result branch
tdd_checkpoint_001
@PreusslerBerlin
Slide 50
Slide 50 text
@PreusslerBerlin
Let’s
talk
Android
Slide 51
Slide 51 text
violating
the most basics
principles
of Software Engineering!
flickr.com/photos/cobblucas/4831501753, Stop by Lucas Cobb, CC by 2.0
Slide 52
Slide 52 text
@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
Slide 53
Slide 53 text
@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
Slide 54
Slide 54 text
@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
Slide 55
Slide 55 text
@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
Slide 56
Slide 56 text
@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
Slide 57
Slide 57 text
@PreusslerBerlin
Separation of
concerns
(1974)
Slide 58
Slide 58 text
@PreusslerBerlin
D.R.Y.
Slide 59
Slide 59 text
@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)
Slide 60
Slide 60 text
@PreusslerBerlin
Slide 61
Slide 61 text
@PreusslerBerlin
Clean
Code
Slide 62
Slide 62 text
@PreusslerBerlin
S.O.L.I.D.
Slide 63
Slide 63 text
@PreusslerBerlin
View
architecture
Slide 64
Slide 64 text
@PreusslerBerlin
MVP
View Presenter Model
Slide 65
Slide 65 text
@PreusslerBerlin
MVVM
View ViewModel Model
Slide 66
Slide 66 text
@PreusslerBerlin
Live coding
• viewmodel
@PreusslerBerlin
Slide 67
Slide 67 text
@PreusslerBerlin
Live coding
• Result branch
tdd_checkpoint_002
@PreusslerBerlin
Slide 68
Slide 68 text
@PreusslerBerlin
Lets talk about Activities
• now the tricky part
starts
https://www.flickr.com/photos/muchadoaboutnothing/438734626
Slide 69
Slide 69 text
@PreusslerBerlin
Android SDK under test
• Android classes can be loaded on JVM:
build/generated/mockable-android-XX.jar
• No more finals!
Slide 70
Slide 70 text
@PreusslerBerlin
Android SDK under test
• Empty methods with default return values
android {
…
testOptions {
unitTests.returnDefaultValues = true
}
-> no code will run
Slide 71
Slide 71 text
@PreusslerBerlin
Live coding
• Classic Activity
@PreusslerBerlin
Slide 72
Slide 72 text
@PreusslerBerlin
Live coding
• Result branch
tdd_checkpoint_003a
@PreusslerBerlin
Slide 73
Slide 73 text
@PreusslerBerlin
Dependency
Injection
Slide 74
Slide 74 text
@PreusslerBerlin
Dependency Inversion
Inversion of Control
Dependency Injection
?
?
?
Slide 75
Slide 75 text
@PreusslerBerlin
The Dependency Inversion Principle
High level entities
should not depend on
low level details.
Slide 76
Slide 76 text
@PreusslerBerlin
Inversion of Control
Who initiates a message
Hollywood's Law:
don't call me, I'll call you.
Slide 77
Slide 77 text
@PreusslerBerlin
Inversion of Control
Stop using new
Slide 78
Slide 78 text
@PreusslerBerlin
Inversion of Control
Common implementations:
● Factory
● Service Locator
●
Dependency Injection
Slide 79
Slide 79 text
@PreusslerBerlin
Inversion of Control
Common implementations:
● Factory
tracker = Factory.createTracker()
● Service Locator
● Dependency Injection
Slide 80
Slide 80 text
@PreusslerBerlin
Inversion of Control
Common implementations:
● Factory
● Service Locator
tracker = Locator.get(Tracker.class)
● Dependency Injection
Slide 81
Slide 81 text
@PreusslerBerlin
Inversion of Control
Common implementations:
● Factory
● Service Locator
● Dependency Injection
@Inject Tracker tracker;
Slide 82
Slide 82 text
@PreusslerBerlin
Live coding
• Activity with injection
@PreusslerBerlin
Slide 83
Slide 83 text
@PreusslerBerlin
Live coding
• Result branch
tdd_checkpoint_003b
@PreusslerBerlin
Slide 84
Slide 84 text
@PreusslerBerlin
Rotation
Slide 85
Slide 85 text
@PreusslerBerlin
Rotation
• Many different solutions
• Let’s go with view model from
Architecture Components
Slide 86
Slide 86 text
@PreusslerBerlin
Rotation
• Many different solutions
• Let’s go with view model from
Architecture Components
@PreusslerBerlin
Problem
• Needs a FragmentActivity
public static ViewModelProvider
of(FragmentActivity activity) {
L
Slide 89
Slide 89 text
@PreusslerBerlin
The problem of v4… Activity
• mockable.jar not existing for libraries,
including support library L
-> real code will run
Slide 90
Slide 90 text
@PreusslerBerlin
Live coding
• FragmentActivity
@PreusslerBerlin
Slide 91
Slide 91 text
@PreusslerBerlin
Live coding
• Result branch
tdd_checkpoint_004a
@PreusslerBerlin
Slide 92
Slide 92 text
@PreusslerBerlin
Live coding
• ViewModel
@PreusslerBerlin
Slide 93
Slide 93 text
@PreusslerBerlin
Live coding
• Result branch
tdd_checkpoint_004b
@PreusslerBerlin
Slide 94
Slide 94 text
@PreusslerBerlin
What’s left?
• Activities needs to be declared in manifest
• Permissions for older devices (internet)
Slide 95
Slide 95 text
@PreusslerBerlin
Live coding
• Manifest via lint
lintOptions {
check 'Registered'
warningsAsErrors true
}
* Has issues with Gradle Plugin 3.0 and Kotlin
@PreusslerBerlin
Slide 96
Slide 96 text
@PreusslerBerlin
Live coding
• Manifest via lint
Slide 97
Slide 97 text
@PreusslerBerlin
Live coding
• Result branch
tdd_checkpoint_005
@PreusslerBerlin
Slide 98
Slide 98 text
@PreusslerBerlin
Live coding
• Zero tolerance policy very valuable
Slide 99
Slide 99 text
@PreusslerBerlin
What’s left?
• Permissions for older devices (internet)
Slide 100
Slide 100 text
@PreusslerBerlin
An
alternative
Slide 101
Slide 101 text
@PreusslerBerlin
Lets talk about data binding
• Testing android view classes are hard
• So let’s make sure we don’t need to test them!
Slide 102
Slide 102 text
@PreusslerBerlin
Data binding full picture
XML ViewModel
bind
Slide 103
Slide 103 text
@PreusslerBerlin
Data binding…
who is the view?
• Solves the one
big android question once
and for all
Slide 104
Slide 104 text
@PreusslerBerlin
ViewModel in Data Binding
Slide 105
Slide 105 text
@PreusslerBerlin
ViewModel in data binding
Slide 106
Slide 106 text
@PreusslerBerlin
Live coding
• Data binding
@PreusslerBerlin
Slide 107
Slide 107 text
@PreusslerBerlin
Live coding
• Result branch
tdd_checkpoint_006
@PreusslerBerlin
Slide 108
Slide 108 text
@PreusslerBerlin
How do I…
Slide 109
Slide 109 text
@PreusslerBerlin
.. show a toast
class SeriesViewModel : Viewmodel() {
…
@Bindable
var error = ObservableField()
Slide 110
Slide 110 text
@PreusslerBerlin
.. show a toast
viewModel.error.addOnPropertyChangedCallback(
object : OnPropertyChangedCallback() {
override fun onPropertyChanged(…) {
showToast(viewModel.error.get()
}
})
Slide 111
Slide 111 text
@PreusslerBerlin
.. show a toast (alternative)
@BindingAdapter("showError")
fun ViewGroup.onErrorAppeared(error: String?){
errorString?.let {
showToast(context, error))
}
}
Slide 112
Slide 112 text
@PreusslerBerlin
Data binding full picture
XML
Activity ViewModel
(un)bind
bind
Life cycle
aware class (un)bind
Slide 113
Slide 113 text
@PreusslerBerlin
Whats left?
• UI: text size, button text…
Slide 114
Slide 114 text
@PreusslerBerlin
What about Espresso
• problem:
• need to be fast!
• need to run continuously
• fast feedback
Slide 115
Slide 115 text
@PreusslerBerlin
Live coding
• Espresso
@PreusslerBerlin
Slide 116
Slide 116 text
@PreusslerBerlin
Live coding
• Result branch
tdd_checkpoint_007
@PreusslerBerlin
Slide 117
Slide 117 text
@PreusslerBerlin
What about
Robolectric?
Slide 118
Slide 118 text
@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
Slide 119
Slide 119 text
@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
Slide 120
Slide 120 text
@PreusslerBerlin
Android today
• Projects not designed to be testable
have often need for Robolectric
Slide 121
Slide 121 text
@PreusslerBerlin
Time to
wrap up
Slide 122
Slide 122 text
@PreusslerBerlin
Limitations
• Custom view and animation code might get too
hard
Slide 123
Slide 123 text
@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
Slide 124
Slide 124 text
@PreusslerBerlin
"Test only if you would
want it to work.”
Kent Beck
Slide 125
Slide 125 text
@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?
Slide 126
Slide 126 text
@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?
Slide 127
Slide 127 text
@PreusslerBerlin
Remember!
• Never write new functionality without test first
• Tests need to be refactored and clean!
Slide 128
Slide 128 text
@PreusslerBerlin
TDD on Android?
• its possible!
• its fun!
https://www.flickr.com/photos/chefranden/14838138493
Slide 129
Slide 129 text
@PreusslerBerlin
TDD on Android?
Unit tests (even with TDD)
are NOT the only tests you need
Slide 130
Slide 130 text
@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
Slide 131
Slide 131 text
@PreusslerBerlin
Tools used
• Protected onCreate call in Kotlin:
github.com/
dpreussler/android-tdd-utils
• Test friendly dependency Injection:
github.com/
stephanenicolas/toothpick
Slide 132
Slide 132 text
@PreusslerBerlin
Tools used
• Some test utils code for FragmentActivity,
check TestUtils.kt
github.com/
sporttotal-tv/android-tdd-workshop
Slide 133
Slide 133 text
@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/