56
Android Unit Testing @mobileLarson @_openKnowledge Lars Röwekamp | CIO New Technologies

Keine Ausreden mehr: Unit-Tests in Android

Embed Size (px)

Citation preview

Android Unit Testing

@mobileLarson @_openKnowledge

Lars Röwekamp | CIO New Technologies

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

„Wer testet ist feige!“„Wenn ich den Test

selber schreibe, macht es doch eh keinen Sinn!“

„Ich weiß doch, dass mein Code

funktioniert!“„Leider keine Zeit für Unit Tests!“

Warum (Unit)testen?

Darum (Unit)testen!

„If You don’t test Android, Android will test You.“

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

„(Unit) Testing is not about finding Bugs.“

(Zitat: alter weiser Mann)

Darum (Unit)testen!

Broken Window Effect

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

0%(Nutzer in %, die eure App in einem Emulator verwenden)

Anforderungen an Testabdeckung

‣ Alles „Offensichtliche“ plus …

‣… Activity LifeCycle Events & Config Changes ‣… Datenbank & FileSystem Zugriff ‣… Umwelteinflüsse & Störungen ‣… zu unterstützende Device Konfigurationen ‣… zu unterstützende Versionen, Locales, …

Was (Unit)testen?

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Herausforderungen des Android-Testing

‣ Tests auf Device/Emulator sind langsambuild, deploy, make & drink coffee, run

‣ android.jar bietet nur Stub ImplementierungError java.lang.RuntimeException

‣ Framework LimitierungenFinal Classes, Static Classes, Static Methods

Warum Unit testen?

benötigen Android

Alle Tests für die Android App

laufen auf JVMDevice Emulator

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Warum Unit testen?

Android Unit Testing Unit Test Support

AndroidJUnitRunner Espresso & Friends

Robloectric

Android Unit Testing Unit Test Support AndroidJUnitRunner Espresso & Friends

Robloectric

‣ Unit Test ‣ Integration Test ‣ Acceptance Tests ‣ Stress Tests

Wo bewegen wir uns?

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Unit Test

‣ Testen der korrekten Funktionsweise einzelner, isolierter Komponenten

‣ automatisiert ‣ regelmäßig ‣ wiederholbar

Wo bewegen wir uns?

Dependencies?

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Unit 1

Unit 2

Unit 3

Component Test Case

Unit 1

Unit 2

Unit 3

Dependencies?

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Unit 1

Unit 2

Unit 3

Component Test Case

Unit 1

Unit 2

Unit 3

mocked

mocked

Unit Tests & Mocks

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Wie kann ich Android für Unit Tests mocken?

‣ Android Gradle Plugin 1.1+ ‣ Verzeichnis /test für Unit Tests ‣ android.jar + Mockito

„stubben“ von Android Framework Dependencies

Unit Tests & Mocks

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

@RunWith(MockitoJUnitRunner.class) public class MockTestSample { private static final String EXPECTED_STRING = "HELLO WORLD"; @Mock Context mMockContext; @Test public void readStringFromContext_LocalizedString() { // Given a mocked Context injected into the object under test... when(mMockContext.getString(R.string.hello_word)).thenReturn(EXPECTED_STRING); ResourceHanlder myObjectUnderTest = new ResourceHanlder(mMockContext); // ...when the string is returned from the object under test... String result = myObjectUnderTest.getHelloWorldString(); // ...then the result should be the expected one. assertThat(result, is(EXPECTED_STRING)); }}

Injection

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

apply plugin: 'com.android.application'android { ... // more to come ... LATER! testOptions { unitTests.returnDefaultValues = true // Danger! } } dependencies { // Dependencies for Unit-Testing testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-all:2.0.2-beta'}

app/build.gradleUnit Tests & Mocks

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

btw: ReportsUnit Tests & Mocks

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Wo sind die Grenzen?

‣ eigene Units sind (stark) mit Android gekoppeltEinsatz von Mockito reicht nicht

‣ Stubbing statischer MethodenLog.*, TextUtils.*, ...

Unit Tests & Mocks

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Lösungen

‣ eigene Units sind (stark) mit Android gekoppeltApp-Struktur-Design überdenken, On-Device testen

‣ Stubbing statischer MethodenWrappen, PowerMockito, unitTests.returnDefaultValues = true

Unit Tests & Mocks

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Unit Tests & Mocks

„Mockito & PowerMock in Action“

IST DAS SCHON DAS ENDE?

Unit Test PLUS

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Instrumentation Tests

‣ Testing Support LibraryBestandteil des Android Support Repositories

‣ Instrumentieren die „App under Test“Test-App läuft parallel zur App auf dem Device/Emulator

‣ Laufen mit AndroidJUnitRunnerTests unter /androidTest, JUnit 3 & JUnit 4

Unit Test PLUS

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Unit Test PLUS

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Was ist mit instrumentieren gemeint?

‣ Control Hocks in das Android SystemApplication Loading, LifeCycle Management

‣Mocks für Android Komponenten Context, ContentResolver, Service, Application, Dialog, …

‣ App under Test & Test App laufen im selben Prozess

Unit Test PLUS

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

ApplicationTestCase

AndroidTestCase

junit.framework.TestCase

ActivityInstrumentationTestCase2

ActivityUnitTestCase

LoaderTestCase

ProviderTestCase2

ServiceTestCase

InstrumentationTestCase

ActivityTestCase

Unit Test PLUS

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

apply plugin: 'com.android.application'android { ... defaultConfig{ ... testInstrumentationRunner ‘android.support.test.runner.AndroidJUnitRunner‘ } } dependencies { ... // AndroidJUnitRunner dependencies androidTestCompile ‘com.android.support.test:runner:0.2‘}

app/build.gradle

Android Unit Testing Unit Test Support

AndroidJUnitRunner Espresso & Friends

Robloectric

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Wieso AndroidJUnitRunner?

‣ Neuer Test-Runner für Androidersetzt InstrumentationTestRunner, JUnit 3 & JUnit 4 Support

‣ InstrumentationRegestry als SchnittstelleArguments, Context, TargetContext, Instrumentation

‣ Special FeaturesTest Filtering, Intent Monitoring/Stubbing, Lifecycle Monitoring

AndroidJUnitRunner

AndroidJUnitRunner

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

@RunWith(AndroidJUnit4.class) public class ResourceHandlerTest { ... @Before public void initResourceHandler() { Context targetContext = InstrumentationRegistry.getTargetContext(); mResourceHandler = new ResourceHandler(targetContext); } @Test public void checkGetHelloWorldString() { String helloWorldString = mResourceHandler.getHelloWorldString(); assertThat(helloWorldString, is(equalTo(EXPECTED_STRING))); } @After public void releaseResourceHandler() { ... } }

AndroidJUnitRunner

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

@RunWith(AndroidJUnit4.class) public class SpecialFeatureOnSpecialDeviceTest { ... @SdkSuppress(minSdkVersion=20) @Test public void featureWithMinSdk20() { // Tests needs some special feature from SDK 15 or above … ... } @RequiresDevice @Test public void someDeviceSpecificFeature() { // Tests needs some special feature from REAL device ... ... } }

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

„AndroidJUnitRunner & Co.“

AndroidJUnitRunner

AndroidJUnitRunner

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

@Beforepublic void runBeforeTest() throws Exception { // injection MUST occur before super.setUp() (AndroidJUnitRunner) injectInstrumentation(InstrumentationRegistry.getInstrumentation()); super.setUp(); // Basically the new AndroidJUnitRunner prevents any Handler from being // created on the Instrumentation worker thread. getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { Intent intent = newIntent(getInstrumentation().getTargetContext(), MainActivity.class); mActivity = startActivity(intent, null, null); } }); ... }

Boilerplate

@Rules

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

@LargeTest@RunWith(AndroidJUnit4.class) public class MainActivityWithRuleUnitTest { @Rule public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity, true, // initialTouchMode true); // use default intent?

// No need for @Before or @After - but possible if needed

@Test public void checkWhateverYouWant() { // place some test code here …

... }

}

@Rules

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

apply plugin: 'com.android.application'android { ... defaultConfig{ ... testInstrumentationRunner ‘android.support.test.runner.AndroidJUnitRunner‘ } } dependencies { ... // AndroidJUnitRunner dependencies androidTestCompile ‚com.android.support.test:runner:0.2‘ androidTestCompile ‘com.android.support.test:rules:0.2‘}

app/build.gradle

Android Unit Testing Unit Test Support

AndroidJUnitRunner Espresso & Friends

Robloectric

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Espresso

Was würde der User tun?

‣ View findenonView( Matcher )

‣ Aktion „performen“perform( ViewAction )

‣ Status „checken“check( ViewAssertion )

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Espresso

‣ View findenonView( Matcher )

‣ Aktion „performen“perform( ViewAction )

‣ Status „checken“check( ViewAssertion )

// Test, ob View-Element da ist onView(withId(R.id.xyz)) .check(matches(isDisplayed()));

// Click auf View-Element onView(withId(R.id.xyz)) .perform(click());

// Daten in einer ListView onData(matches(someObject)) .perform(click());

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Espresso

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Espresso

Espresso

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

@RunWith(AndroidJUnit4.class) public class EspressoActivityTest { ... @Rule public ActivityTestRule<HelloWorldActivity> mActivityRule = new ActivityTestRule<>(HelloWorldActivity.class,true,false); @Test public void checkWhatever() { // espresso allows to define a start intent per test Intent intent = new Intent(); intent.putExtra(HelloWorldActivity.EXTRA_DATA, DATA); mActivityRule.launchActivity(intent); // check that view elements value was set with intent data

onView(withId(SOME_ID)).check(matches(isDisplayed())); onView(withId(SOME_ID)).check(matches(withText(DATA.getData())));

} }

use own Intent

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Espresso

Geht da noch mehr?

‣ Espresso Contrib APIsDrawerActions, TimePickerActions, …

‣ Espresso-Intents„Mockito“ für Intents, Stub, Validation

Espresso

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

apply plugin: 'com.android.application'android { ... defaultConfig{ ... testInstrumentationRunner ‘android.support.test.runner.AndroidJUnitRunner‘ } } dependencies { ... // Espresso androidTestCompile ‚com.android.support.test.espresso:espresso-core:2.1 androidTestCompile ‚com.android.support.test.espresso:espresso-contrib:2.1 androidTestCompile ‚com.android.support.test.espresso:espresso-intents:2.1

}

app/build.gradle

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

„Espresso & Co.“

AndroidJUnitRunner

Android Unit Testing Unit Test Support

AndroidJUnitRunner Espresso & Friends

Robolectric

benötigen Android

Alle Tests für die Android App

laufen auf JVMDevice Emulator

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Warum Unit testen?

SLOW!FAST

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Robolectric

Off-Device Testing

‣Mock für Android.jarjava.lang.RuntimeException: Stub!

‣ Simulation des Device Component LifeCycle Simulation

‣ Shadow KlassenMock-Android plus Test-Features

Robolectric

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

// Robolectric test class for MyActivity @RunWith(RobolectricTestRunner.class) public class MyActivityTest {

@Test public void btnClk_shouldChangeText() throws Exception { Activity activity = Robolectric.buildActivity(MyActivity.class).create().get();

Button pressMeButton = (Button) activity.findViewById(R.id.press_me_button); TextView textView = (TextView) activity.findViewById(R.id.results_text_view); pressMeButton.performClick(); String resultsText = textView.getText().toString(); assertThat(resultsText, equalTo(EXPECTED_TEXT)); } }

Robolectric

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

apply plugin: 'com.android.application'android { ... defaultConfig{ ... } } dependencies { ... // Espresso androidTestCompile ‚‘org.robolectric:robolectric:2.1

}

app/build.gradle

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Robolectric

Special Features

‣ Test ConfigurationSDK Version, App, Resource-Path

‣ Add-On Modulesupport-v4, maps, play-service, …

‣ Extensibility Shadows, TestRunner, …

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

„Robolectric“

AndroidJUnitRunner

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Status Quo nach 60 Minuten …

‣ „echtes“ Unit-Testing nur schwer möglichMockito, PowerMock, Robolectric

‣ Unit-Testing via Support-LibAndroidJUnitRunner, Espresso, JUnit 4, Activity, Service, Provider

‣ Instrumentation-Testing via Support-LibActivity-Übergänge, Android-Abhängigkeiten

Android Unit Testing

By the way …

„Documentation and Stability

really, really sucks!“

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Always remember

„If You don’t test Android, Android will test You.“

Android Unit Testing by Lars Röwekamp (open knowledge GmbH)

Android Unit Testing

@mobileLarson @_openKnowledge

Lars Röwekamp | CIO New Technologies