#MBLTdev: Автоматическое тестирование Android приложений c...

Post on 26-Jun-2015

660 views 0 download

Tags:

description

#MBLTdev: Конференция мобильных разработчиков Спикер: Автоматическое тестирование Android приложений c любовью http://mbltdev.ru/

Transcript of #MBLTdev: Автоматическое тестирование Android приложений c...

Lovely Testing of Android AppsEffective and safe auto testing of Android apps. From Siberia with Love.

Alex Korovyansky #{MBLT}DEV

October 28, 2014

Do we need auto testing for our Android apps?

Yes, we do! If we want to be successful in long term.But, it should be safe (no flaky tests and buggy tools) and effective (easy to extend, fast to write, fast to run)!

Problems

• Complexity of mobile UI testing

Problems

• Complexity of mobile UI testing

• Mobile artefacts (GPS, sensors, etc)

Problems

• Complexity of mobile UI testing

• Mobile artefacts (GPS, sensors, etc)

• Huge number of different devices

Problems

• Complexity of mobile UI testing

• Mobile artefacts (GPS, sensors, etc)

• Huge number of different devices

• Big number of different approaches

Problems

• Complexity of mobile UI testing

• Mobile artefacts (GPS, sensors, etc)

• Huge number of different devices

• Big number of different approaches

• Lack of official documentation

Problems

• Complexity of mobile UI testing

• Mobile artefacts (GPS, sensors, etc)

• Huge number of different devices

• Big number of different approaches

• Lack of official documentation

• Official solution is friend of slow and flaky tests

Problems

• Complexity of mobile UI testing

• Mobile artefacts (GPS, sensors, etc)

• Huge number of different devices

• Big number of different approaches

• Lack of official documentation

• Official solution is friend of slow and flaky tests

• …

Problems

Solutions

• Instrumentation Framework (by Google)

Solutions

• Instrumentation Framework (by Google)

• Robotium (by Jayway)

Solutions

• Instrumentation Framework (by Google)

• Robotium (by Jayway)

• UIAutomator (by Google)

Solutions

• Instrumentation Framework (by Google)

• Robotium (by Jayway)

• UIAutomator (by Google)

• Robolectric (by ExtremeLabs)

Solutions

• Instrumentation Framework (by Google)

• Robotium (by Jayway)

• UIAutomator (by Google)

• Robolectric (by ExtremeLabs)

• Espresso (by Google)

Solutions

• Instrumentation Framework (by Google)

• Robotium (by Jayway)

• UIAutomator (by Google)

• Robolectric (by ExtremeLabs)

• Espresso (by Google)

• Double-Espresso (by Jake Wharton)

Solutions

• Instrumentation Framework (by Google)

• Robotium (by Jayway)

• UIAutomator (by Google)

• Robolectric (by ExtremeLabs)

• Espresso (by Google)

• Double-Espresso (by Jake Wharton)

• … AssertJ-Android, Burst, Spoon …

Solutions

Robolectric

Espresso

SV

Watch a movie — http://www.imdb.com/title/tt0892782/

Double Espresso

• Battery

• Location

• Mobile network

• Multitouch

• Sensors

• …

How test Android artefacts?

• Dagger <- currently best one solution for DI @ Android

• Dagger 2 *

• RoboGuice

• Guice

• PicoContainer

• Spring

• …

Use Dependency Injection!

Component <<interface>> Service

ServiceImplAssembler

<<create>>

Watch Introduction to Dagger — http://youtu.be/tPs1e3dQ6FU

Dependency Injection

Auto Testing

&

• Simple app for registration to #MBLTDev

• RESTful communication with backend

• Location helper for city field

• Open Source code

• End-to-end auto tests

• Just demo app, fork it on GitHub

Meet Demo App

• Form fields behaviour

• Behaviour of auto-locate button

• Transformation of form data to request

• End-to-end user scenarios: 1. Successful registration 2. Registration is closed error 3. Network error

• Test on different devices

What we can test?

Setup Double Espresso

!androidTestCompile 'com.squareup.spoon:spoon-client:1.1-r3' { exclude group: 'com.squareup.dagger' }

! defaultConfig { testInstrumentationRunner "com.google.android.apps.common.testing" + ".testrunner.GoogleInstrumentationTestRunner" } !

build.gradle

public class MBLTDevDemoApp extends Application { ! @Override public void onCreate() { super.onCreate(); setModules(getStartModules()); } ! public void inject(Object component) { this.objectGraph.inject(component); } ! public Object[] getModules() { return modules.toArray(new Object[modules.size()]); } ! public void plusModule(Object module) { this.modules.add(module); this.objectGraph = objectGraph.plus(module); } ! public void setModules(Object[] modules) { this.modules = new ArrayList<Object>(Arrays.asList(modules)); this.objectGraph = ObjectGraph.create(modules); } ! protected Object[] getStartModules() { return Modules.list(this); } }

Daggerized app

Daggerize test apppublic class MBLTDevDemoTestApp extends MBLTDevDemoApp { @Override protected Object[] getStartModules() { return new Object[]{new MBLTDevDemoModule(this), new MBLTDevDemoTestModule()}; } }

@Module( injects = { GetTicketActivity.class, … } ) public class MBLTDevDemoTestModule { ! @Provides @Singleton public LocationService provideLocationService() { return new TestLocationService(); } ! @Provides @Singleton public TestLocationService provideLocationService( LocationService locationService) { return locationService; } ! … }

@Module( complete = false, library = true, overrides = true, injects = { DemoTests.class} ) public class MBLTDevDemoModule { ! @Provides @Singleton public LocationService provideLocationService( Context context) { return new RealLocationService(context); } ! … }

Customize TestRunnerpackage com.medlert.android.transport.env; !… !public class MBLTDevDemoTestRunner extends GoogleInstrumentationTestRunner { ! @Override public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return Instrumentation.newApplication(MBLTDevDemoTestApp.class, context); } !} !

defaultConfig { testInstrumentationRunner “com.mbltdev.lovelyandroid.env.testrunner” } !

build.gradle

DI in super testpublic class MBLTDevDemoInstrumentationTestCase2<T extends Activity> extends ActivityInstrumentationTestCase2<T> { ! private Object[] modules; ! public MBLTDevDemoInstrumentationTestCase2(Class<T> activityClass) { super(activityClass); } ! @Override protected void setUp() throws Exception { super.setUp(); this.modules = getApp().getModules(); getApp().inject(this); } ! @Override protected void tearDown() throws Exception { getApp().setModules(modules); super.tearDown(); } ! protected MBLTDevDemoApp getApp() { return (MBLTDevDemoApp) (getInstrumentation().getTargetContext().getApplicationContext()); } }

First cup of coffeepublic class InitialStateTests extends ActivityInstrumentationTestCase2<GetTicketActivity> { ! public FormFieldsTests() { super(GetTicketActivity.class); } ! @Override protected void setUp() throws Exception { super.setUp(); getActivity(); } ! @SmallTest public void testFormInitialState() throws Exception { onView(withId(R.id.first_name_field)).check(matches(isDisplayed())); onView(withId(R.id.last_name_field)).check(matches(isDisplayed())); onView(withId(R.id.city_field)).check(matches(isDisplayed())); onView(withId(R.id.company_field)).check(matches(isDisplayed())); onView(withId(R.id.position_field)).check(matches(isDisplayed())); onView(withId(R.id.get_ticket)).check(matches(isDisplayed())); } !}

Coffee with Dependency Injectionpublic class BehaviourTests extends ActivityInstrumentationTestCase2<GetTicketActivity> { ! @Inject TestLocationService testLocationService; @Inject TestBackendService testBackendService; @Inject @TestOperationLengthMs testOperationLengthMs; … ! @SmallTest public void testAutoLocateButton() throws Exception { testLocationService.setLocation(TestLocations.moscow()); getActivity(); onView(withId(R.id.auto_locate)).perform(click()); Thread.sleep(testOperationLengthMs); onView(withId(R.id.field_city)).check(withText(“Moscow”)); } ! @SmallTest public void testFormNetworkRequest() throws Exception { Request chuckNorrisRequest = TestRequests.chuckNorris(); onFillForm(chuckNorrisRequest); onView(withId(R.id.get_ticket)).perform(click()); assertEquals(chuckNorrisRequest, testBackendService.getLastRequest()); } !}

Cup with end-to-end user scenariospublic class EndToEndTests extends ActivityInstrumentationTestCase2<GetTicketActivity> { @Inject TestBackendService testBackendService; @Inject @TestOperationLengthMs testOperationLengthMs; … ! @SmallTest public void testSuccessfulRegistration() throws Exception { testBackendService.setGetTicketResponse(Response.success()); getActivity(); onFillForm(TestRequests.chuckNorris()); onView(withId(R.id.get_ticket)).perform(click()); Thread.sleep(testOperationLengthMs); onView(withText(“Congrats! You are registered!”)).check(isDisplayed()); } ! @SmallTest public void testClosedRegistration() throws Exception { testBackendService.setGetTicketResponse(Response.alreadyClosed()); getActivity(); onFillForm(TestRequests.chuckNorris()); onView(withId(R.id.get_ticket)).perform(click()); Thread.sleep(testOperationLengthMs); onView(withText(“Oops… Registration is already closed!”)).check(isDisplayed()); } !}

Run Tests!

• Spoon — https://github.com/square/spoon

• SauceLabs — https://saucelabs.com/

• TestObject — https://testobject.com/

• Appurify — http://appurify.com/

• Appthwack — http://appthwack.com/

• TestDroid — http://testdroid.com

• …

Testing on different real devices

Conclusions• It’s good time to start testing of your apps

• Hope, you will love it

• Select Double Espresso or Robolectric

• Dependency Injection and Test Automation are big friends

• More things is coming

• Keep your hand on a pulse!

Links 101• Espresso

https://code.google.com/p/android-test-kit/wiki/Espresso

• Double Espressohttps://github.com/JakeWharton/double-espresso

• Robolectric https://github.com/robolectric/robolectric

• Dagger https://github.com/square/dagger

• Demo Apphttps://github.com/AlexKorovyansky/mbltdev-demo

Watch and talk• Introduction to Espresso

http://youtu.be/T7ugmCuNxDU

• Introduction to Dagger http://youtu.be/tPs1e3dQ6FU

• GTAC 2013http://bit.ly/gtac2013

• GTAC 2014http://bit.ly/gtac2013

• Make a talk in GDG RUhttp://bit.ly/gdgru_online_talk

Q&A Alex Korovyansky korovyansk@gmail.com http://about.me/korovyansk