writing testable android apps

43
Writing testable Android apps Tomáš Kypta @TomasKypta

Upload: tomas-kypta

Post on 16-Jan-2017

281 views

Category:

Technology


0 download

TRANSCRIPT

Writing testable Android apps

Tomáš Kypta @TomasKypta

What does it mean testable?

What does it mean testable?

• Test it quickly during development

• Test units in isolation

• There’s a way to provide mocks

How Google imagined unit testing?

Instrumentation tests

What “standard” Android app code looks like?

“Standard” Android app

• check Android SDK samples & documentation

• or Google’s iosched app

• business logic in Activities, Fragments, Services, …

• view and presenter effectively mixed together

This is actually the structure recommended

by Google!

How to make it testable?

Abstract away from Android APIs

Android APIs

• difficult to test and mock

• we don’t have creation of Android components under control

Abstracting Android APIs

• we need some boilerplate to get away

• remove all business logic from Android component classes

• isolate business logic into presenters, managers, POJOs, …

• when possible use MVP pattern

Abstracting Android APIs

• further improvements

• avoid or wrap Android API

Clean code

• try to write clean code

• avoid methods having hundreds of lines

• avoid Activities/Fragments having thousands of lines

And how to test it?

• our presenters, POJOs, managers, providers, etc. are simple to test

• Android components are stripped off of business logic

• nothing important to test remains there

Bonus Advantages

• better readability

• better maintainability

Android Unit Tests

Android Unit Tests

• Added rather recently

• You need some work to make them really useful.

• Dependency injection

• Mocking

Dependency Injection

Dependency Injection

• the main ingredient

• avoid “new” keyword

• Dagger 2 FTW

Dagger 2• by Google

• evolution of Dagger 1 (by Square)

• no reflection

• generated code in compile time

• constructor and field injection

Dagger 2

• Constructor injection

private ProviderC mProviderC; private ProviderD mProviderD; @Inject public ProviderA(ProviderC providerC, ProviderD providerD) { mProviderC = providerC; mProviderD = providerD; }

Dagger 2• Field injection

public class MainActivity extends AppCompatActivity { @Inject ProviderA mProviderA; @Inject ProviderB mProviderB;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((MyApp) getApplication()).getAppComponent() .injectMainActivity(this); }}

Dagger 2

• prefer constructor injection wherever possible

• you can test the unit in isolation

• providing mocks is a piece of cake

Dagger 2• Some catches when using field injection

• Construct the whole component

• Implicit provisions doesn’t support overrides!!

public class ProviderA { … @Inject public ProviderA(ProviderC providerC) { mProviderC = providerC; }}

Dagger 2• Some catches when using field injection

• Make explicit provisions @Module public class AppModule { @Provides @Singleton ProviderB provideProvider2(ProviderC providerC) { return new ProviderB(providerC); } @Provides ProviderD provideProvider4() { return new ProviderD(); } }

• Beware marking constructors with @Inject when providing explicitly

• may create unwanted double provision

Mocking

Mocking

• Android framework has some historical things

• package android.test.mock

• for instrumentation tests

Android APIs & Unit Test

• historically

• java.lang.RuntimeException: Stub!

• now

• mockable android.jar

• Method ... not mocked.

Mocking

• it’s important to “program to an interface”

• avoid final and static

Mocking

@RunWith(JUnit4.class) public class ProviderATest { @Mock ProviderC mProviderC; @Mock ProviderD mProviderD; ProviderA mProviderA; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Mockito.when(mProviderC.getString()).thenReturn("hello"); Mockito.when(mProviderD.getString()).thenReturn("world"); mProviderA = new ProviderA(mProviderC, mProviderD); }

…}

Mocking static methods

Static methods• avoid it as much as possible

• things like TextUtils.isEmpty() can be painful

• alternatives, e.g. Apache commons StringUtils.isEmpty()

• PowerMock to the rescue

• still better to avoid static

Negatives of testable apps?

Negatives?

• increase the number of methods and classes

• duplication of Android interfaces/classes

• bloated constructors with lots of dependencies

Dev App Builds

Dev App Builds

• How to use dev overrides in dev buils?

• utilize Android’s Gradle build

• Product Flavors can be used for that

Recommended Libraries

Recommended Libraries

• Dagger 2

• Retrofit

• RxJava / RxAndroid

• Mortar

Recommended Libraries - for testing

Q&A

Thank You