Pro Yearly is on sale from $80 to $50! »

Espressoテストコードの同期処理を究める / Synchronization capabilities of Espresso

17997dee8a3da090f62d8cf8c494d8ff?s=47 TOYAMA Sumio
February 07, 2018

Espressoテストコードの同期処理を究める / Synchronization capabilities of Espresso

DroidKaigi 2018のセッション「Espressoテストコードの同期処理を究める」(2018/2/8 15:40- @ Room5) の発表スライドです。

17997dee8a3da090f62d8cf8c494d8ff?s=128

TOYAMA Sumio

February 07, 2018
Tweet

Transcript

  1. Espresso    2018.02.08  (sumio_tym) DroidKaigi 2018 @

    Day1 Room5 15:40-
  2. /3- p'&: " 2 (TOYAMA Sumio) @sumio_tym (Twi0er) / @sumio

    (GitHub) p#0: DeNA SWET  (So<ware Engineer in Test) p$(:    p : pSTAR (  ,.) p@IT1 %*)    (UI Automator / Appium+!) 2
  3. -   pAndroidUI #Espresso 0/7'683%  9 1) 

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

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

     4. &"'# 5. sleep()% $ 5
  6. 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 -'
  7. 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
  8. Espresso      (# $"OFF) p 

      p   p % !  8
  9. Espresso psrc/androidTest/java  pAndroid Test Support Library  9 @RunWith(AndroidJUnit4.class)

    public class MyEspressoTest { @Rule public ActivityTestRule<...> activityActivityTestRule = ...; ... }
  10. Espresso API   OK perform()check()  10 onView(ViewMatcher) .perform(ViewAction)

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

    .check(ViewAssertion); p ViewMatcher  View  p ViewAction p  ViewViewAssertion 
  12.    Espresso   p(* p+, ' p!#"&

    $% ) p  p ' pAPI  12
  13.    Espresso    p p !

     p   p  p  pAPI  13 !
  14.   1. Espresso   2. Espresso  3.

        4. " # 5. sleep()!   14
  15. !'")(. +$ &/0  - 3, *  (https://goo.gl/poLgkZ) p

     pAsyncTask   p%2IdlingResource &(#1) 15
  16.       pHandler p Thread Executor

    p p 16 H Android J
  17. ',(.-4  1)+  $56 3820 (https://goo.gl/poLgkZ) p"&% pAsyncTask!#& %

    p  IdlingResource  17 AsyncTask1/  *7  Espresso  
  18.  pEspresso!&"('+/  AsyncTask,  pOkHttpRxJava!&"(#.) p  IdlingResource$0 

    !&"(-%#.*  18
  19.  1. Espresso  2. Espresso  3.  

     4.  5. sleep()   19
  20. .2 / "$  ! /   2 pandroid.support.test.espresso

    pIdlingResource pIdlingRegistry .2 #   1. IdlingResource ' )( IdlingRegistry &% 20
  21. IdlingResource 21 public interface IdlingResource { public String getName(); public

    boolean isIdleNow(); // # %& ? ("* + ! IdlingResource!  $),' "
  22. IdlingResource 22 //  public void registerIdleTransitionCallback( ResourceCallback callback); public

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

    interface ResourceCallback { public void onTransitionToIdle(); } } !"   callback.onTransitionToIdle()   (  )#!!
  24. IdlingResource  24 Espresso   IdlingResource onView()... isIdleNow() UI

      onTransitionToIdle() registerIdleTransitionCallback(this) false   UI
  25. IdlingResource 25 private ResourceCallback callback; private boolean idle = false;

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

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

    idle = false; public boolean isIdleNow() { if (callback != null && idle) { callback.onTransitionToIdle(); } return idle; }      
  28. IdlingRegistry IdlingResource  / p     

    28 private IdlingRegistry reg; ... reg = IdlingRegistry.getInstance();
  29. IdlingRegistry IdlingResource' %#/$" 29 p IdlingResource ( !&  )

    reg.register(idlingResource); p IdlingResource (%#  ) reg.unregister(idlingResource);
  30. IdlingRegistry I P pIdlingRegistry: Espresso 3.0  p API: pEspresso.registerIdlingResources()

    pEspresso.unregisterIldingResources() p ) ./7 / :/. p ) A    p / 1::7 ( /: / 66 4/ 65 30
  31.  p&+',/*(3-  IdlingResource% # )7 1 p3 $!")7 

    p    )78 pIdlingResource)7   IdlingRegistry64/.5  p02   64  31
  32.   1. Espresso   2. Espresso" 3. 

    !   4.  5. sleep() $  # 32
  33. : 1. : CountingIdlingResource 2. CountingIdlingResource p EJ 3. p

    . IdlingResource p R 4 33
  34. CountingIdlingResource%$ p'# !(   ")   pincrement(): ")

      ('# & ) pdecrement(): ")   ('#!(& ) 34   0 
  35. 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     
  36. 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   "
  37. E : 1. E: CountingIdlingResource 2. CountingIdlingResource p  

      3. E p IdlingResource p 4 . 37
  38. RxJava2 API RxJavaPlugins setScheduleHandler( Function<? super Runnable, ? extends Runnable>)

    :  & $  ( #'  )! %"  38
  39. RxJava2  API 39 RxJavaPlugins .setScheduleHandler(oldAction ->{ Log.d(TAG,"  ");

    return () -> { Log.d(TAG,""); oldAction.run(); Log.d(TAG,""); }; }); p  API    
  40. setScheduleHandler() 2 . .CountingIdlingResource  2 .CountingIdlingResource  2 40

    1 ⇔ 0
  41.   41 //  private CountingIdlingResource resource = new

    CountingIdlingResource("Rx"); private IdlingRegistry registry = IdlingRegistry.getInstance();
  42.  42 @Before public void setUp() { RxJavaPlugins.setScheduleHandler( oldAction ->

    () -> { try { resource.increment(); oldAction.run(); } finally { resource.decrement(); } }); registry.register(resource); }    
  43.  43 @After public void tearDown() { registry.unregister(resource); RxJavaPlugins.reset(); }

  44. : A RxJava A) 44 // ( // A )

    ) @Rule public ActivityTestRule<MyActivity> activityRule = new ActivityTestRule<>( MyActivity.class, false, false); @Before public void setUp() { ... // A) registry.register(resource); activityRule.launchActivity(null); }
  45. : / 3 45 RxView.clicks(button) .debounce(3, TimeUnits.SECONDS) .subscribe(v -> textView.setText("OK"));

    3 ! OK      
  46.    :  RxJava Espresso   in

    Rx Ja Night Vol.2 h7ps://goo.gl/GBG8r1 46
  47. : 1. : CountingIdlingResource 2. CountingIdlingResource p EJ 3. 

    p . IdlingResource p R 4 47
  48. 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(
  49. idling-concurrent (β ) com.android.support.test.espresso.idling: idling-concurrent:3.0.1 pIdlingScheduledThreadPoolExecutor pScheduledThreadPoolExecutor IdlingResource  p

      49 Espresso IdlingResource 
  50. EspressoIdlingResource  IdlingThreadPoolExecutor pOkH3p   50 OkHttpClient okHttpClient =

    new OkHttpClient.Builder() .dispatcher( new Dispatcher( new IdlingThreadPoolExecutor( "OkHttp", ...))) .build();
  51. 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 "(
  52. 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
  53. UriIdlingResource! 53 Client GET A <img src="B"> ... <img src="C">

    GET B and C 1  2            
  54.    pRxIdler https://github.com/square/RxIdler pRxJava!   p "API

    () 54
  55. $ 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
  56. R : E 1. : CountingIdlingResource 2. CountingIdlingResource p 3.

    p J IdlingResource p     56
  57. 0# '... pIdlingResource "(:  pIdlingResource .*/&-:  p ,$

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

    +" ( $  58
  59. 1 2$("   pIdlingResource *:  pIdlingResource /-/&.: 

     p # /- + pIdlingResource  p0!)  pEspresso',API IdlingResource  /- % 59
  60.   +*  p'$.-#)implementation /( com.android.support.test.espresso: espresso-idling-resource:3.0.1 p, 3

     (Espresso"%,!&) pIdlingRegistry pIdlingResource pCountingIdlingResource 60
  61.  p CountingIdlingResource/EF =, p$<*/E20%A. p 72? RxJavaC(381> & )-H@

    p GIdlingResource/EH@ p IdlingResource+0#;6'4  :9 p"# !;6D5B 61
  62.   1. Espresso   2. Espresso  3.

        4. " #! 5. sleep()  62
  63. IdlingResource ) ) IdlingResource ⭕) ❌ ❌ E ( (

    ❌ 63
  64. IdlingResource(2     p%+0 $ 4,  

    &. (RxJava!#*'/ ) p31API" "#-)    &. 64
  65. IdlingResource UI Automator  pSelenium 5 API View 1 65

    // "OK" // assertThat( uiDevice.wait( Until.hasObject(By.text("OK")), 5000L), is(true));
  66. 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()); }
  67. UI Automator # p $ )uiDevice.wait() # p Until 

     %"!& pfindObject() pfindObjects() pgone() phasObject() 67 uiDevice.wait(' %",  ()
  68. 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
  69. UI Automator: Tips 69 // R.id.star // 5 assertThat( uiDevice.wait(Until.hasObject(

    By.res(toResName(R.id.star))), 5000L), is(true));
  70. 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
  71.  pIdlingResource#* !  p),"%' p   ( 

     UI AutomatorAPI&+$ 71
  72. 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
  73. &!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
  74.  Espresso UI'$ !  # p $" pIdlingResource( 

    p)&IdlingResource(*% p+ IdlingResource    $ 74
  75.  Espresso        

    75