Slides from DroidCon NYC 2014. Covers some ideas around extracting business logic from the app to enable easier unit testing. Much more detail available on my blog (kevinrschultz.com)
4. Treading Water
Time spent on features dwindles
Infrequent releases due to high
QA costs
Heisenbug whack-a-mole
Fear of change
Infrastructure
Bugs
Features
5. Drowning
Platform Changes
Business Pivot
Competitors
Panic
How many regressions will
you cause when
implementing Material
Design?
8. Unlikely to be better next time
Clean code is necessary but insufficient for
maintainability
We are state of the art today, what about next year?
Why do Android apps always seem to become
unmaintainable?
9. Android was initially for Apps
Documentation & samples are small scale
Old build system was deficient
Testing tools are still sub-par
Framework was not designed for testability
Performance trumped clean code
11. Software Entropy
“As a system is modified, its disorder, or entropy, always
increases. This is known as software entropy.”
- Ivar Jacobson
“Legacy code is code without tests”
- Michael Feathers
13. System Thinking
How will each component be tested over its lifetime?
What leads to the minimum cost of development
including QA time?
If a library makes it easy to add a feature but harder to
test, is it worth it?
If a tool breaks CI, is it worth it?
14. Testing Strategy
Customers in Production
Manual QA by Team
Unit Tests
Integration tests
Automated UI Tests
15. Manual Testing
Low initial cost
Need to acceptance test every feature at least once
Never can be completely eliminated
Does not scale with complexity
Extremely expensive long term
16. QA Options
Helps make manual testing
cheaper & more effective
Network connectivity
Environment
Mock Data
17. QA Specs
Look at Gherkin / Cucumber for examples
Helps to make tests more repeatable
Can use to outsource testing to other firms
Painful & expensive
18. Automated UI Testing
Appium / UIAutomator type tests
Usually leveraging the accessibility IDs
Expensive, flakey
Can be layered onto most apps after the fact
19. Functional Testing
Most Android ‘unit testing’ examples are functional
Robolectric & InstrumentationTests
Generally scoped to an Activity / Fragment / Service
Time consuming to run and nearly as flakey as UI tests
Useful but should be minimized
20. Functional Testing Challenges
Generic Problems
Asynchronous
Network based
!
!
Android Specific
Lifecycle
Context
Platform singletons
21. Functional Test in Theory
insertMockDataIntoPreferences(conditions)
waitForActivityAndFragmentToInitialize()
waitForDataToLoad()
label = findViewById()
assertEquals(conditions, label.getText());
Preconditions
Boilerplate
Actual Test
testWindSpeedLabel_MetricSystem
23. Pitfalls for a Functional Test
Is the user logged in?
Wait for Activity, Fragment lifecycle to load screen
Wait for network call to finish
Wait for data to load from DB
Find the TextView
Finally, check the TextView’s content & color
24. Unit Testing
Narrow in scope, each test is 1 case for 1 method
Do not test Android platform, therefore run quickly
Cheap to write if architecture is designed for testing
Flexible for refactoring
Should be 80% of your tests
25. Unit Test in Theory
testWindSpeedLabel_MetricSystem
given(conditions)
assertEquals(conditions, label.getText());
40. Wrapping Shared Preferences
+ Only 1 place to test interaction w/ SharedPreferences
+ Handle versioning
+ Keys don’t leak out
+ Can now mock SharedPreferences w/o Context
- More code
50. View Models
Add presentation logic to underlying models
Combine multiple models
Do not have a concept of Activity/Fragment lifecycle
Do not have a reference to View
51. Using a ViewModel
Fragment is responsible for loading models,
networking, finding views, threading
ViewModel is responsible for business logic
Fragment should fail in all cases
60. Use Cases
Great for screens displaying detail about an object
Not ideal for collections of items, but Adapters can be
tested similarly
Not a great fit for screens with many interactions
62. Presenters
Same general idea as ViewModel, but includes
reference to View
Requires coordinating with Activity/Fragment lifecycle
Potential for Context leaks if not handled correctly
More full featured, can take ownership of fetching data,
threading, network calls, etc
66. Fragment Continued
User interactions delegated to presenter
Can test using the same methods on the presenter
67. Presenter Continued
All business logic belongs in presenter
Calls back to view for update, remember the view may
be gone
68. Use Cases
Can handle state changes caused by user interaction
very well
Really should use Espresso / InstrumentationTests to
ensure that click handlers call appropriate methods in
presenter
69. Summary
You are building a system, not just an app
Cost of testing over long term becomes extremely expensive
Find ways to keep the platform at arms length
Consider how to test each component from the start
76. Long Term Cost
70
52.5
35
17.5
0
Manual Unit Tests UI Tests Test Fixtures
1 2 3 4 5 6
77. Stage 1 -> No desire for tests
Stage 2 -> Desire tests, but can’t build the test
suite
Stage 3 -> Have a test suite
Stage 4 -> Nirvana. Bugs find themselves.
78. But I Don’t Have Business
Logic!
“It’s just simple data in the UI!”
Not for long (PDP examples)
Creating a place for the tests to go from the start
makes it easy to add them (UUID example)
Every single thing is small, but the sum of testing them
all is expensive