Slide 1

Slide 1 text

Effective Espresso Misumi_Rize Roppongi.aar #2 (Nov. 17, 2015)

Slide 2

Slide 2 text

Log.d("introduction", me); → Twitter @Misumi_Rize → GitHub @MisumiRize → I'm not Android APK developer

Slide 3

Slide 3 text

My previous presentation

Slide 4

Slide 4 text

First of all: androidTest != Espresso

Slide 5

Slide 5 text

Espresso is... → High level wrapper of Instrumentation → High level wrapper of InputEvent → View assertion

Slide 6

Slide 6 text

You can test them with Espresso → View interaction → Activity transition

Slide 7

Slide 7 text

Espresso Pros. → You can stub out unnecessary APIs → Simple synchronous interaction API → Built-in matchers

Slide 8

Slide 8 text

Espresso Cons. → Requires slow simulator or real device → Long test problem → Built-in matchers

Slide 9

Slide 9 text

Use case of Espresso → Activity integration test → Do not test everything using Espresso → Avoid writing too many tests using Espresso

Slide 10

Slide 10 text

Start Using Espresso

Slide 11

Slide 11 text

build.gradle android { defaultConfig { // ... testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' } // ... packagingOptions { exclude 'LICENSE.txt' } } dependencies { // ... androidTestCompile 'com.android.support.test:runner:0.4.1' androidTestCompile 'com.android.support.test:rules:0.4.1' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1' }

Slide 12

Slide 12 text

Run with JUnit 4 @RunWith(AndroidJUnit4Runner.class) public class MainActivityTest { @Rule public ActivityTestRule activityRule = new ActivityTestRule<>(MainActivity.class); @Test public void submitButtonIsDisplayed() { Espresso.onView(ViewMatchers.withId(R.id.submit_button)) .check(ViewAssertions.isDisplayed()); } }

Slide 13

Slide 13 text

Note → @Rule is just JUnit lifecycle hook → You can also launch Activity manually → You can also assert regular JUnit way

Slide 14

Slide 14 text

Composite ViewMatchers → ViewMatchers.hasText() → ViewMatchers.hasSibling() → allOf(), oneOf()

Slide 15

Slide 15 text

Capture Intents

Slide 16

Slide 16 text

build.gradle dependencies { // ... androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1' }

Slide 17

Slide 17 text

Intent matching @Test public void testTransitingToBrowser() { Espresso.onView(ViewMatchers.withId(R.id.launch_browser)) .perform(ViewActions.click()); Intents.intended( Matchers.allOf( IntentMatchers.hasAction(IsEqual.equalTo(Intent.ACTION_VIEW)), IntentMatchers.hasData("https://google.com") ) ); }

Slide 18

Slide 18 text

Async Handling

Slide 19

Slide 19 text

build.gradle dependencies { // ... androidTestCompile 'com.android.support.test.espresso:espresso-idling-resource:2.2.1' }

Slide 20

Slide 20 text

public class MyIdlingResource implements IdlingResource { private ResourceCallback resourceCallback; private idle = true; @Override public String getName() { return IntentServiceIdlingResource.class.getName(); } @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { this.resourceCallback = resourceCallback; } @Override public boolean isIdleNow() { if (idle && resourceCallback != null) { resourceCallback.onTransitionToIdle(); } return idle; } public void setIdle(boolean idle) { this.idle = idle; } }

Slide 21

Slide 21 text

On test @Test public void testHandleAsync() { MyIdlingResource idlingResource = new MyIdlingResource(); Espresso.registerIdlingResources(idlingResource); // Do something // Do not forget unregister idlingResource Espresso.unregisterIdlingResources(idlingResource); }

Slide 22

Slide 22 text

Don't Repeat Yourself

Slide 23

Slide 23 text

DRY → Separate content from code → Make refactoring easy

Slide 24

Slide 24 text

@Test public void submitButtonIsDisplayed() { submitButton().check( ViewAssertions.isDisplayed() ); } private ViewInteraction submitButton() { return Espresso.onView( ViewMatchers.withText("Submit") ); }

Slide 25

Slide 25 text

Random Data

Slide 26

Slide 26 text

Use Faker → More natural user data → Bootstrappable (zip code, address) → Internet-friendly (email, domain)

Slide 27

Slide 27 text

build.gradle dependencies { // ... androidTestCompile 'com.github.javafaker:javafaker:0.6' }

Slide 28

Slide 28 text

private Faker faker; @Before public void setUp() { faker = new Faker(); } @Test public void inputEmailAddress() { editEmailAddress() .perform(ViewActions.sendKeys(emailAddress())); } private String emailAddress() { return faker.internet().emailAddress(); }

Slide 29

Slide 29 text

Use With Lambda

Slide 30

Slide 30 text

Lambda on Android → Shorten anonymous class declaration → Retrolambda, Kotlin, Groovy

Slide 31

Slide 31 text

Check with Lambda @Test public void textIsCorrect() { myTextView() .check((View view, NoMatchingViewException e) -> if (e != null) { Assert.fail(e.getMessage()); } Assert.assertEquals( ((TextView) view).getText().toString(), "Hello, world" ); }) }

Slide 32

Slide 32 text

But little dirty hack is needed with Kotlin... private val activityRule = ActivityTestRule(MainActivity::class.java) @Rule public fun getActivityRule(): ActivityTestRule = activityRule

Slide 33

Slide 33 text

Page Object Pattern?

Slide 34

Slide 34 text

Does Page Object Pattern adapt? → I think page object pattern is too huge → Espresso: Integration test → Selenium (Appium): End-to-end test

Slide 35

Slide 35 text

Conclusion → Espresso is high level wrapper of Instrumentation → There are many tips to make tests solid → Do not test everything using Espresso

Slide 36

Slide 36 text

→ Rocket Espresso Cellini V3 by Scott Schiller