Slide 1

Slide 1 text

Espresso 2018.02.08 (sumio_tym) DroidKaigi 2018 @ Day1 Room5 15:40-

Slide 2

Slide 2 text

/3- p'&: " 2 (TOYAMA Sumio) @sumio_tym (Twi0er) / @sumio (GitHub) p#0: DeNA SWET (So

Slide 3

Slide 3 text

- pAndroidUI #Espresso 0/7'683% 9 1) p.5,(1) 3 0/7'"$2 Espresso ! sleep()4+&*

Slide 4

Slide 4 text

! 1. Espresso 2. Espresso# 3. " 4. ($ )% 5. sleep() ' & 4

Slide 5

Slide 5 text

1. Espresso 2. Espresso! 3. 4. &"'# 5. sleep()%$ 5

Slide 6

Slide 6 text

Espresso pAndroid)/UI https://d.android.com/training/testing/espresso/index.html p%3$28#3 pEspresso Test Recorder pDroidKaigi 2017 ( Espresso 07 https://goo.gl/D73jBN p69+", "4 p.5!*1& 6 https://developer.android.com/training/testing/espresso/index.html -'

Slide 7

Slide 7 text

Espresso (https://goo.gl/VSHigS) prepositoriesgoogle() pdependencies pconfiguration: androidTestImplementation pgroup: com.android.support.test.espresso pmodule: espresso-core pversion: 3.0.1 ptestInstrumentationRunner: "android.support.test.runner.AndroidJUnitRunner" 7

Slide 8

Slide 8 text

Espresso (#$"OFF) p p p % ! 8

Slide 9

Slide 9 text

Espresso psrc/androidTest/java pAndroid Test Support Library 9 @RunWith(AndroidJUnit4.class) public class MyEspressoTest { @Rule public ActivityTestRule<...> activityActivityTestRule = ...; ... }

Slide 10

Slide 10 text

Espresso API OK perform()check() 10 onView(ViewMatcher) .perform(ViewAction) .check(ViewAssertion); p ViewMatcher View p ViewAction p ViewViewAssertion

Slide 11

Slide 11 text

Espresso API API https://goo.gl/hx6YPo 11 onView(ViewMatcher) .perform(ViewAction) .check(ViewAssertion); p ViewMatcher View p ViewAction p ViewViewAssertion

Slide 12

Slide 12 text

Espresso p(* p+, ' p!#"& $% ) p p ' pAPI 12

Slide 13

Slide 13 text

Espresso p p ! p p p pAPI 13 !

Slide 14

Slide 14 text

1. Espresso 2. Espresso 3. 4. " # 5. sleep()! 14

Slide 15

Slide 15 text

!'")(. +$ &/0 - 3,* (https://goo.gl/poLgkZ) p pAsyncTask p%2IdlingResource &(#1) 15

Slide 16

Slide 16 text

pHandler p Thread Executor p p 16 H Android J

Slide 17

Slide 17 text

',(.-4 1)+ $56 3820 (https://goo.gl/poLgkZ) p"&% pAsyncTask!#& % p IdlingResource 17 AsyncTask1/ *7 Espresso

Slide 18

Slide 18 text

pEspresso!&"('+/ AsyncTask, pOkHttpRxJava!&"(#.) p IdlingResource$0 !&"(-%#.* 18

Slide 19

Slide 19 text

1. Espresso 2. Espresso 3. 4. 5. sleep() 19

Slide 20

Slide 20 text

.2 / "$ ! / 2 pandroid.support.test.espresso pIdlingResource pIdlingRegistry .2 # 1. IdlingResource ' )( IdlingRegistry &% 20

Slide 21

Slide 21 text

IdlingResource 21 public interface IdlingResource { public String getName(); public boolean isIdleNow(); // # %& ? ("* + ! IdlingResource! $),' "

Slide 22

Slide 22 text

IdlingResource 22 // public void registerIdleTransitionCallback( ResourceCallback callback); public interface ResourceCallback { public void onTransitionToIdle(); } }

Slide 23

Slide 23 text

IdlingResource 23 // public void registerIdleTransitionCallback( ResourceCallback callback); public interface ResourceCallback { public void onTransitionToIdle(); } } !" callback.onTransitionToIdle() ( )#!!

Slide 24

Slide 24 text

IdlingResource 24 Espresso IdlingResource onView()... isIdleNow() UI onTransitionToIdle() registerIdleTransitionCallback(this) false UI

Slide 25

Slide 25 text

IdlingResource 25 private ResourceCallback callback; private boolean idle = false; public void registerIdleTransitionCallback( ResourceCallback callback) { this.callback = callback } //

Slide 26

Slide 26 text

IdlingResource 26 // private ResourceCallback callback; // private boolean idle = false; public void backgroundWorkDone() { idle = true; callback.onTransitionToIdle(); } //

Slide 27

Slide 27 text

IdlingResource 27 // private ResourceCallback callback; // private boolean idle = false; public boolean isIdleNow() { if (callback != null && idle) { callback.onTransitionToIdle(); } return idle; }

Slide 28

Slide 28 text

IdlingRegistry IdlingResource / p 28 private IdlingRegistry reg; ... reg = IdlingRegistry.getInstance();

Slide 29

Slide 29 text

IdlingRegistry IdlingResource'%#/$" 29 pIdlingResource ( !& ) reg.register(idlingResource); pIdlingResource (%# ) reg.unregister(idlingResource);

Slide 30

Slide 30 text

IdlingRegistry I P pIdlingRegistry: Espresso 3.0 p API: pEspresso.registerIdlingResources() pEspresso.unregisterIldingResources() p ) ./7 / :/. p ) A p / 1::7 ( /: / 66 4/ 65 30

Slide 31

Slide 31 text

p&+',/*(3- IdlingResource% # )7 1 p3 $!")7 p )78 pIdlingResource)7 IdlingRegistry64/.5 p02 64 31

Slide 32

Slide 32 text

1. Espresso 2. Espresso" 3. ! 4. 5. sleep() $ # 32

Slide 33

Slide 33 text

: 1. : CountingIdlingResource 2. CountingIdlingResource p EJ 3. p . IdlingResource p R 4 33

Slide 34

Slide 34 text

CountingIdlingResource%$ p'# !( ") pincrement(): ") ('# & ) pdecrement(): ") ('#!(& ) 34 0

Slide 35

Slide 35 text

CountingIdlingResource 35 AtomicInteger counter = new AtomicInteger(0); volatile ResourceCallback callback; @Override public boolean isIdleNow() { return counter.get() == 0; } @Override public void registerIdleTransitionCallback( ResourceCallback callback) { this.callback = callback; } ※ Google's Maven Repository h4ps://goo.gl/aMUS5n

Slide 36

Slide 36 text

CountingIdlingResource# 36 public void increment() { counter.getAndIncrement(); } public void decrement() { int count = counter.decrementAndGet(); if (count == 0 && null != callback) { // 0 ! callback.onTransitionToIdle(); } } } ※ Google's Maven Repository h4ps://goo.gl/aMUS5n "

Slide 37

Slide 37 text

E : 1. E: CountingIdlingResource 2. CountingIdlingResource p 3. E p IdlingResource p 4 . 37

Slide 38

Slide 38 text

RxJava2 API RxJavaPlugins setScheduleHandler( Function super Runnable, ? extends Runnable>) : &$ ( #' )!%" 38

Slide 39

Slide 39 text

RxJava2 API 39 RxJavaPlugins .setScheduleHandler(oldAction ->{ Log.d(TAG," "); return () -> { Log.d(TAG,""); oldAction.run(); Log.d(TAG,""); }; }); p API

Slide 40

Slide 40 text

setScheduleHandler() 2 . .CountingIdlingResource 2 .CountingIdlingResource 2 40 1 ⇔ 0

Slide 41

Slide 41 text

41 // private CountingIdlingResource resource = new CountingIdlingResource("Rx"); private IdlingRegistry registry = IdlingRegistry.getInstance();

Slide 42

Slide 42 text

42 @Before public void setUp() { RxJavaPlugins.setScheduleHandler( oldAction -> () -> { try { resource.increment(); oldAction.run(); } finally { resource.decrement(); } }); registry.register(resource); }

Slide 43

Slide 43 text

43 @After public void tearDown() { registry.unregister(resource); RxJavaPlugins.reset(); }

Slide 44

Slide 44 text

: A RxJava A) 44 // ( // A ) ) @Rule public ActivityTestRule activityRule = new ActivityTestRule<>( MyActivity.class, false, false); @Before public void setUp() { ... // A) registry.register(resource); activityRule.launchActivity(null); }

Slide 45

Slide 45 text

: / 3 45 RxView.clicks(button) .debounce(3, TimeUnits.SECONDS) .subscribe(v -> textView.setText("OK")); 3 ! OK

Slide 46

Slide 46 text

: RxJava Espresso in Rx Ja Night Vol.2 h7ps://goo.gl/GBG8r1 46

Slide 47

Slide 47 text

: 1. : CountingIdlingResource 2. CountingIdlingResource p EJ 3. p . IdlingResource p R 4 47

Slide 48

Slide 48 text

idling-concurrent (β ) com.android.support.test.espresso.idling: idling-concurrent:3.0.1 pIdlingThreadPoolExecutor pThreadPoolExecutor IdlingResource( pThreadPoolExecutor new IdlingThreadPoolExecutor new register unregister ) 48 Espresso IdlingResource(

Slide 49

Slide 49 text

idling-concurrent (β ) com.android.support.test.espresso.idling: idling-concurrent:3.0.1 pIdlingScheduledThreadPoolExecutor pScheduledThreadPoolExecutor IdlingResource p 49 Espresso IdlingResource

Slide 50

Slide 50 text

EspressoIdlingResource IdlingThreadPoolExecutor pOkH3p 50 OkHttpClient okHttpClient = new OkHttpClient.Builder() .dispatcher( new Dispatcher( new IdlingThreadPoolExecutor( "OkHttp", ...))) .build();

Slide 51

Slide 51 text

Espresso!$IdlingResource' IdlingThreadPoolExecutor pOkH3p %# 51 OkHttpClient okHttpClient = new OkHttpClient.Builder() .dispatcher( new Dispatcher( new IdlingThreadPoolExecutor( "OkHttp", ...))) .build(); OkHttp ThreadPoolExecutor (Dispatcher. executorService()"() & h3ps://goo.gl/UVdQ9B "(

Slide 52

Slide 52 text

idling-net (β ) com.android.support.test.espresso.idling: idling-net:3.0.1 pUriIdlingResource pWebView/ View/I p / p / beginLoad(uri) endLoad(uri) 52 Espresso IdlingResource

Slide 53

Slide 53 text

UriIdlingResource! 53 Client GET A ... GET B and C 1 2

Slide 54

Slide 54 text

pRxIdler https://github.com/square/RxIdler pRxJava! p "API () 54

Slide 55

Slide 55 text

$ pOkH$p Idling Resource h$ps://github.com/JakeWharton/okh$p-idling-resource pOkH$p '#% pRetrofit+RxJava! p Issue #10 & " h$ps://github.com/JakeWharton/okh$p-idling-resource/issues/10 55

Slide 56

Slide 56 text

R : E 1. : CountingIdlingResource 2. CountingIdlingResource p 3. p J IdlingResource p 56

Slide 57

Slide 57 text

0#'... pIdlingResource"(: pIdlingResource.*/&-: p ,$ ) + 57 (IdlingResource /%! )

Slide 58

Slide 58 text

#&*. ! https://goo.gl/w15nur pIdlingResource ': pIdlingResource -)/%,: p +" ( $ 58

Slide 59

Slide 59 text

12$(" pIdlingResource *: pIdlingResource /-/&.: p # /- + pIdlingResource p0!) pEspresso',API IdlingResource /- % 59

Slide 60

Slide 60 text

+* p'$.-#)implementation /( com.android.support.test.espresso: espresso-idling-resource:3.0.1 p, 3 (Espresso"%,!&) pIdlingRegistry pIdlingResource pCountingIdlingResource 60

Slide 61

Slide 61 text

p CountingIdlingResource/EF =, p$<*/E20%A. p 72? RxJavaC(381>& )-H@ p GIdlingResource/EH@ p IdlingResource+0#;6'4 :9 p"# !;6D5B 61

Slide 62

Slide 62 text

1. Espresso 2. Espresso 3. 4. " #! 5. sleep() 62

Slide 63

Slide 63 text

IdlingResource ) ) IdlingResource ⭕) ❌ ❌ E ( ( ❌ 63

Slide 64

Slide 64 text

IdlingResource(2 p%+0$4, &. (RxJava!#*'/ ) p31API" "#-) &. 64

Slide 65

Slide 65 text

IdlingResource UI Automator pSelenium 5 API View 1 65 // "OK" // assertThat( uiDevice.wait( Until.hasObject(By.text("OK")), 5000L), is(true));

Slide 66

Slide 66 text

UI Automator p com.android.support.test.uiautomator: uiautomator-v18:2.1.3 p @Before 66 private UiDevice uiDevice; @Before public void setUp() throws Exception { uiDevice = UiDevice.getInstance( InstrumentationRegistry .getInstrumentation()); }

Slide 67

Slide 67 text

UI Automator # p $ )uiDevice.wait()# p Until %"!& pfindObject() pfindObjects() pgone() phasObject() 67 uiDevice.wait('%", ()

Slide 68

Slide 68 text

UI Automator: Tips 68 /** I */ String toResName(int resId) { return InstrumentationRegistry .getTargetContext() .getResources() .getResourceName(resId); } ID(int)UI p UI Automator D p ID I

Slide 69

Slide 69 text

UI Automator: Tips 69 // R.id.star // 5 assertThat( uiDevice.wait(Until.hasObject( By.res(toResName(R.id.star))), 5000L), is(true));

Slide 70

Slide 70 text

UI Automator: Tips 70 // R.id.text // " UiObject2 textView = uiDevice.wait(Until.findObject( By.res(toResName(R.id.text))), 5000L); assertThat(textView.wait( Until.textEquals("OK"), 5000L), is(true)); AND UI p UiObject2 wait

Slide 71

Slide 71 text

pIdlingResource#* ! p),"%' p ( UI AutomatorAPI&+$ 71

Slide 72

Slide 72 text

URL p Kenta Kase RxJava Espresso in DroidKaigi Prelude https://goo.gl/zUQEFi p Iñaki VillarEspresso, Beyond the basicsin 360|AnDev 2017 https://goo.gl/FG7sLf p Espresso 3.0.1 p https://goo.gl/VidZGa (espresso-core) p https://goo.gl/aMUS5n (espresso-idling-resource) p Espresso Idling Resources https://goo.gl/PGzjWe 72

Slide 73

Slide 73 text

&!URL ($,) p RxJava-( )Espresso " in Rx Ja Night Vol.2 h;ps://goo.gl/GBG8r1 p %. Espresso+'*" in DroidKaigi 2017 h;ps://goo.gl/D73jBN p % UI Automator# in DroidKaigi 2016 h;ps://goo.gl/NQV8GP 73

Slide 74

Slide 74 text

Espresso UI'$ !# p $" pIdlingResource( p)&IdlingResource(*% p+ IdlingResource $ 74

Slide 75

Slide 75 text

Espresso 75