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 p 69+", "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 p IdlingResource ( !&  ) reg.register(idlingResource); p IdlingResource (%#  ) 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) :  & $  ( #'  )! %"  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

1 2$("   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