building maintainable android apps (droidcon nyc 2014)

78
Building Maintainable Android Applications Kevin Schultz (@kevinrschultz) DroidCon NYC 2014 kevinrschultz.com github.com/krschultz/AndroidWeatherBuoyDemo

Upload: kevin-schultz

Post on 14-Nov-2014

250 views

Category:

Software


2 download

DESCRIPTION

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)

TRANSCRIPT

Page 1: Building Maintainable Android Apps (DroidCon NYC 2014)

Building Maintainable Android Applications!

Kevin Schultz (@kevinrschultz) DroidCon NYC 2014 !kevinrschultz.com github.com/krschultz/AndroidWeatherBuoyDemo

Page 2: Building Maintainable Android Apps (DroidCon NYC 2014)

Apps vs Applications

Page 3: Building Maintainable Android Apps (DroidCon NYC 2014)
Page 4: Building Maintainable Android Apps (DroidCon NYC 2014)

Treading WaterTime spent on features dwindles

Infrequent releases due to high QA costs

Heisenbug whack-a-mole

Fear of change

Bugs

Features

Infrastructure

Page 5: Building Maintainable Android Apps (DroidCon NYC 2014)

Drowning

Platform Changes

Business Pivot

Competitors

Panic

How many regressions will you cause when

implementing Material Design?

Page 6: Building Maintainable Android Apps (DroidCon NYC 2014)

Refactor! Rewrite!

Page 7: Building Maintainable Android Apps (DroidCon NYC 2014)

The codebase is a mess!

Page 8: Building Maintainable Android Apps (DroidCon NYC 2014)

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?

Page 9: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 10: Building Maintainable Android Apps (DroidCon NYC 2014)

You must build a system to counter software entropy

Page 11: Building Maintainable Android Apps (DroidCon NYC 2014)

Software Entropy

“Legacy code is code without tests” - Michael Feathers

“As a system is modified, its disorder, or entropy, always increases. This is known as software entropy.” - Ivar Jacobson

Page 12: Building Maintainable Android Apps (DroidCon NYC 2014)

System

CI Server

Build

UI Tests

Functional Tests

Unit Tests App

Espresso

Page 13: Building Maintainable Android Apps (DroidCon NYC 2014)

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?

Page 14: Building Maintainable Android Apps (DroidCon NYC 2014)

Testing Strategy

Customers in Production

Manual QA by Team

Unit Tests

Integration tests

Automated UI Tests

Page 15: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 16: Building Maintainable Android Apps (DroidCon NYC 2014)

QA Options

Helps make manual testing cheaper & more effective

Network connectivity

Environment

Mock Data

Page 17: Building Maintainable Android Apps (DroidCon NYC 2014)

QA Specs

Look at Gherkin / Cucumber for examples

Helps to make tests more repeatable

Can use to outsource testing to other firms

Painful & expensive

Page 18: Building Maintainable Android Apps (DroidCon NYC 2014)

Automated UI Testing

Appium / UIAutomator type tests

Usually leveraging the accessibility IDs

Expensive, flakey

Can be layered onto most apps after the fact

Page 19: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 20: Building Maintainable Android Apps (DroidCon NYC 2014)

Functional Testing ChallengesGeneric Problems

Asynchronous

Network based

!

!

Android Specific

Lifecycle

Context

Platform singletons

Page 21: Building Maintainable Android Apps (DroidCon NYC 2014)

Functional Test in Theory

assertEquals(conditions, label.getText());

insertMockDataIntoPreferences(conditions)

waitForActivityAndFragmentToInitialize()

label = findViewById()

waitForDataToLoad()

Actual Test

BoilerplatePreconditions

testWindSpeedLabel_MetricSystem

Page 22: Building Maintainable Android Apps (DroidCon NYC 2014)

Functional Test in Practice

Page 23: Building Maintainable Android Apps (DroidCon NYC 2014)

Pitfalls for a Functional TestIs 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

Page 24: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 25: Building Maintainable Android Apps (DroidCon NYC 2014)

Unit Test in Theory

assertEquals(conditions, label.getText());

given(conditions)

testWindSpeedLabel_MetricSystem

Page 26: Building Maintainable Android Apps (DroidCon NYC 2014)

Unit Tests in Practice

Page 27: Building Maintainable Android Apps (DroidCon NYC 2014)

So why is this so hard?

Page 28: Building Maintainable Android Apps (DroidCon NYC 2014)

Activity Fragment Adapter

ForecastActivity ForecastFragment ForecastAdapter ForecastModel

Page 29: Building Maintainable Android Apps (DroidCon NYC 2014)

Keep Android At Arm’s Length

Page 30: Building Maintainable Android Apps (DroidCon NYC 2014)

StrategiesCovered

Dealing with K/V Store

ViewModels, Presenters, Custom Views

Not Covered

Testing network calls

Testing SQLite / ContentProviders / ORMs

Page 31: Building Maintainable Android Apps (DroidCon NYC 2014)

Weather Buoy

Master list of buoys

Detail screen with wind & wave forecast

Setting to choose units

github.com/krschultz/AndroidWeatherBuoyDemo

Page 32: Building Maintainable Android Apps (DroidCon NYC 2014)

ToolsExamples uses only

Android standard JUnit & InstrumentationTests

Mockito

AssertJ

Better tools exist

Espresso

Robolectric

Dagger

Page 33: Building Maintainable Android Apps (DroidCon NYC 2014)

SharedPreferences

Page 34: Building Maintainable Android Apps (DroidCon NYC 2014)

How do you catch this?

Page 35: Building Maintainable Android Apps (DroidCon NYC 2014)

How do you catch this?

Page 36: Building Maintainable Android Apps (DroidCon NYC 2014)

Wrapping Preferences

Page 37: Building Maintainable Android Apps (DroidCon NYC 2014)

Testing Preferences Wrapper

Page 38: Building Maintainable Android Apps (DroidCon NYC 2014)

Using wrapped Preferences

Page 39: Building Maintainable Android Apps (DroidCon NYC 2014)

Using mock Preferences

Page 40: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 41: Building Maintainable Android Apps (DroidCon NYC 2014)

UI Unit Testing Strategies

Page 42: Building Maintainable Android Apps (DroidCon NYC 2014)

Custom Views

Page 43: Building Maintainable Android Apps (DroidCon NYC 2014)

Optional TextView

What happens when we refactor the layout?

How do we know this feature doesn’t break?

Page 44: Building Maintainable Android Apps (DroidCon NYC 2014)

Optional TextView

Page 45: Building Maintainable Android Apps (DroidCon NYC 2014)

Testing Optional TextView

Page 46: Building Maintainable Android Apps (DroidCon NYC 2014)

Instrument View

LabelValue & Units

Page 47: Building Maintainable Android Apps (DroidCon NYC 2014)

Custom View

+ Clean API

+ Reusable view logic

- Compose-ability issues

Page 48: Building Maintainable Android Apps (DroidCon NYC 2014)

ViewModels

Page 49: Building Maintainable Android Apps (DroidCon NYC 2014)

SharedPreferences

Views

ForecastFragment

WaveForecast

ViewsViews

WindForecast

Preferences

API Client

Page 50: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 51: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 52: Building Maintainable Android Apps (DroidCon NYC 2014)

“Presentation” Logic

Colors, visibility

Interaction between multiple views

Formatting / humanizing properties

i18n

Page 53: Building Maintainable Android Apps (DroidCon NYC 2014)

Combining Models

BuoyDetailViewModel

WaveForecast PreferencesWindForecast

Page 54: Building Maintainable Android Apps (DroidCon NYC 2014)

SharedPreferences

Views

ForecastFragment WaveForecast

ViewsViews

WindForecast

Preferences

ViewModel

API Client

Page 55: Building Maintainable Android Apps (DroidCon NYC 2014)

ViewModel Creation

No reference to view at all

All models can be immutable

Page 56: Building Maintainable Android Apps (DroidCon NYC 2014)

Using a ViewModel

Create view model at the end of network call or DB fetch

Pass Views to ViewModel for updating

Page 57: Building Maintainable Android Apps (DroidCon NYC 2014)

Logic in a ViewModel

Page 58: Building Maintainable Android Apps (DroidCon NYC 2014)

PitfallsCreating a ViewModel getter for every view property

Keeping references to views in the ViewModel

Page 59: Building Maintainable Android Apps (DroidCon NYC 2014)

Testing

Page 60: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 61: Building Maintainable Android Apps (DroidCon NYC 2014)

Presenters

Page 62: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 63: Building Maintainable Android Apps (DroidCon NYC 2014)

Views

ForecastFragment WaveForecast

ViewsViews

WindForecast

Preferences

Presenter

View Interface

Page 64: Building Maintainable Android Apps (DroidCon NYC 2014)

Fragment

Page 65: Building Maintainable Android Apps (DroidCon NYC 2014)

Presenter

Page 66: Building Maintainable Android Apps (DroidCon NYC 2014)

Fragment Continued

User interactions delegated to presenter

Can test using the same methods on the presenter

Page 67: Building Maintainable Android Apps (DroidCon NYC 2014)

Presenter Continued

All business logic belongs in presenter

Calls back to view for update, remember the view may be gone

Page 68: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 69: Building Maintainable Android Apps (DroidCon NYC 2014)

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

Page 70: Building Maintainable Android Apps (DroidCon NYC 2014)

Further Reading

www.objc.io/issue-13/viper.html

martinfowler.com/eaaDev/uiArchs.html

Page 71: Building Maintainable Android Apps (DroidCon NYC 2014)

Contact

@kevinrschultz

github.com/krschultz/AndroidWeatherBuoyDemo

kevinrschultz.com

Page 72: Building Maintainable Android Apps (DroidCon NYC 2014)

Questions?

Page 73: Building Maintainable Android Apps (DroidCon NYC 2014)

Backup

Page 74: Building Maintainable Android Apps (DroidCon NYC 2014)

Law of Demeter w/ Context

Page 75: Building Maintainable Android Apps (DroidCon NYC 2014)

Image Attribution

!

Tent -

House - https://www.flickr.com/photos/18702768@N04/2294709357

Cake - https://www.flickr.com/photos/personalcreations/14888675499

Page 76: Building Maintainable Android Apps (DroidCon NYC 2014)

Long Term Cost

0

17.5

35

52.5

70

1 2 3 4 5 6

Manual Unit Tests UI Tests Test Fixtures

Page 77: Building Maintainable Android Apps (DroidCon NYC 2014)

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.

Page 78: Building Maintainable Android Apps (DroidCon NYC 2014)

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