building maintainable android apps (droidcon nyc 2014)

Post on 14-Nov-2014

250 Views

Category:

Software

2 Downloads

Preview:

Click to see full reader

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

Building Maintainable Android Applications!

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

Apps vs Applications

Treading WaterTime spent on features dwindles

Infrequent releases due to high QA costs

Heisenbug whack-a-mole

Fear of change

Bugs

Features

Infrastructure

Drowning

Platform Changes

Business Pivot

Competitors

Panic

How many regressions will you cause when

implementing Material Design?

Refactor! Rewrite!

The codebase is a mess!

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?

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

You must build a system to counter software entropy

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

System

CI Server

Build

UI Tests

Functional Tests

Unit Tests App

Espresso

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?

Testing Strategy

Customers in Production

Manual QA by Team

Unit Tests

Integration tests

Automated UI Tests

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

QA Options

Helps make manual testing cheaper & more effective

Network connectivity

Environment

Mock Data

QA Specs

Look at Gherkin / Cucumber for examples

Helps to make tests more repeatable

Can use to outsource testing to other firms

Painful & expensive

Automated UI Testing

Appium / UIAutomator type tests

Usually leveraging the accessibility IDs

Expensive, flakey

Can be layered onto most apps after the fact

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

Functional Testing ChallengesGeneric Problems

Asynchronous

Network based

!

!

Android Specific

Lifecycle

Context

Platform singletons

Functional Test in Theory

assertEquals(conditions, label.getText());

insertMockDataIntoPreferences(conditions)

waitForActivityAndFragmentToInitialize()

label = findViewById()

waitForDataToLoad()

Actual Test

BoilerplatePreconditions

testWindSpeedLabel_MetricSystem

Functional Test in Practice

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

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

Unit Test in Theory

assertEquals(conditions, label.getText());

given(conditions)

testWindSpeedLabel_MetricSystem

Unit Tests in Practice

So why is this so hard?

Activity Fragment Adapter

ForecastActivity ForecastFragment ForecastAdapter ForecastModel

Keep Android At Arm’s Length

StrategiesCovered

Dealing with K/V Store

ViewModels, Presenters, Custom Views

Not Covered

Testing network calls

Testing SQLite / ContentProviders / ORMs

Weather Buoy

Master list of buoys

Detail screen with wind & wave forecast

Setting to choose units

github.com/krschultz/AndroidWeatherBuoyDemo

ToolsExamples uses only

Android standard JUnit & InstrumentationTests

Mockito

AssertJ

Better tools exist

Espresso

Robolectric

Dagger

SharedPreferences

How do you catch this?

How do you catch this?

Wrapping Preferences

Testing Preferences Wrapper

Using wrapped Preferences

Using mock Preferences

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

UI Unit Testing Strategies

Custom Views

Optional TextView

What happens when we refactor the layout?

How do we know this feature doesn’t break?

Optional TextView

Testing Optional TextView

Instrument View

LabelValue & Units

Custom View

+ Clean API

+ Reusable view logic

- Compose-ability issues

ViewModels

SharedPreferences

Views

ForecastFragment

WaveForecast

ViewsViews

WindForecast

Preferences

API Client

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

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

“Presentation” Logic

Colors, visibility

Interaction between multiple views

Formatting / humanizing properties

i18n

Combining Models

BuoyDetailViewModel

WaveForecast PreferencesWindForecast

SharedPreferences

Views

ForecastFragment WaveForecast

ViewsViews

WindForecast

Preferences

ViewModel

API Client

ViewModel Creation

No reference to view at all

All models can be immutable

Using a ViewModel

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

Pass Views to ViewModel for updating

Logic in a ViewModel

PitfallsCreating a ViewModel getter for every view property

Keeping references to views in the ViewModel

Testing

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

Presenters

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

Views

ForecastFragment WaveForecast

ViewsViews

WindForecast

Preferences

Presenter

View Interface

Fragment

Presenter

Fragment Continued

User interactions delegated to presenter

Can test using the same methods on the presenter

Presenter Continued

All business logic belongs in presenter

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

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

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

Further Reading

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

martinfowler.com/eaaDev/uiArchs.html

Contact

@kevinrschultz

github.com/krschultz/AndroidWeatherBuoyDemo

kevinrschultz.com

Questions?

Backup

Law of Demeter w/ Context

Image Attribution

!

Tent -

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

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

Long Term Cost

0

17.5

35

52.5

70

1 2 3 4 5 6

Manual Unit Tests UI Tests Test Fixtures

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.

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

top related