keine ausreden mehr: unit-tests in android
TRANSCRIPT
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!
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?
‣ 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)
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“
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)
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 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 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());
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
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)
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)