$30 off During Our Annual Pro Sale. View Details »

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

TOYAMA Sumio
February 07, 2018

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

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

TOYAMA Sumio

February 07, 2018
Tweet

More Decks by TOYAMA Sumio

Other Decks in Programming

Transcript

  1. Espresso


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

    View Slide

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

    (Sop$(:
    p :
    pSTAR ( ,.)
    p@IT1%*)
    (UI Automator / Appium+!)
    2

    View Slide

  3. -

    pAndroidUI #Espresso
    0/7'683%

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

    View Slide


  4. !
    1. Espresso

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

    View Slide


  5. 1. Espresso
    2. Espresso!
    3.

    4. &"'#
    5. sleep()%$
    5

    View Slide

  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
    p69+",
    "4
    p.5!*1&

    6
    https://developer.android.com/training/testing/espresso/index.html -'

    View Slide

  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

    View Slide

  8. Espresso


    (#$"OFF)
    p

    p
    p % !
    8

    View Slide

  9. Espresso
    psrc/androidTest/java
    pAndroid Test Support Library

    9
    @RunWith(AndroidJUnit4.class)
    public class MyEspressoTest {
    @Rule
    public ActivityTestRule<...>
    activityActivityTestRule = ...;
    ...
    }

    View Slide

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


    View Slide

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

    p ViewAction
    p ViewViewAssertion

    View Slide



  12. Espresso

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

    p
    '
    pAPI
    12

    View Slide



  13. Espresso

    p
    p !
    p
    p

    p

    pAPI
    13
    !

    View Slide


  14. 1. Espresso
    2. Espresso

    3.

    4. " #
    5. sleep()!
    14

    View Slide

  15. !'")(.

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

    View Slide




  16. pHandler
    p Thread Executor
    p
    p
    16
    H Android
    J

    View Slide

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

    17
    AsyncTask1/
    *7

    Espresso

    View Slide


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

    !&"(-%#.*

    18

    View Slide


  19. 1. Espresso
    2. Espresso

    3.

    4.
    5. sleep()
    19

    View Slide

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

    View Slide

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

    $),'
    "

    View Slide

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

    View Slide

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

    callback.onTransitionToIdle()
    ( )#!!

    View Slide

  24. IdlingResource
    24
    Espresso
    IdlingResource
    onView()...
    isIdleNow()
    UI


    onTransitionToIdle()
    registerIdleTransitionCallback(this)
    false


    UI

    View Slide

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

    View Slide

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




    View Slide

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



    View Slide

  28. IdlingRegistry
    IdlingResource
    /
    p




    28
    private IdlingRegistry reg;
    ...
    reg = IdlingRegistry.getInstance();

    View Slide

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

    (%#
    )
    reg.unregister(idlingResource);

    View Slide

  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

    View Slide


  31. p&+',/*(3-

    IdlingResource% #
    )7
    1
    p3 $!")7

    p

    )78
    pIdlingResource)7
    IdlingRegistry64/.5

    p02
    64

    31

    View Slide



  32. 1. Espresso

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

    View Slide

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

    View Slide

  34. CountingIdlingResource%$
    p'# !(

    ")
    pincrement(): ")

    ('# & )
    pdecrement(): ")
    ('#!(& )
    34

    0

    View Slide

  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


    View Slide

  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
    "

    View Slide

  37. E :
    1. E: CountingIdlingResource
    2. CountingIdlingResource
    p

    3. E
    p IdlingResource
    p 4
    .
    37

    View Slide

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

    )!%"

    38

    View Slide

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

    View Slide

  40. setScheduleHandler()
    2 .

    .CountingIdlingResource
    2

    .CountingIdlingResource
    2
    40
    1

    0

    View Slide


  41. 41
    //

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

    View Slide


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





    View Slide


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

    View Slide

  44. : 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); }

    View Slide

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




    View Slide


  46. :
    RxJava Espresso

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

    View Slide

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

    View Slide

  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(

    View Slide

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

    p
    49
    Espresso IdlingResource

    View Slide

  50. EspressoIdlingResource
    IdlingThreadPoolExecutor
    pOkH3p

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

    View Slide

  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 "(

    View Slide

  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

    View Slide

  53. UriIdlingResource!
    53
    Client
    GET A
    ...

    GET B and C
    1
    2








    View Slide


  54. pRxIdler
    https://github.com/square/RxIdler
    pRxJava!

    p "API
    ()
    54

    View Slide

  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

    View Slide

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


    56

    View Slide

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

    57


    (IdlingResource
    /%!
    )

    View Slide

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

    View Slide

  59. 12$("

    pIdlingResource *:
    pIdlingResource /-/&.:

    p #
    /-
    +
    pIdlingResource
    p0!)
    pEspresso',API IdlingResource
    /- %
    59

    View Slide

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

    View Slide


  61. p CountingIdlingResource/EF
    =,

    p$<*/E20%A.
    p 72? RxJavaC(381>&
    )-H@

    p GIdlingResource/EH@

    p IdlingResource+0#;6'4
    :9

    p"# !;6D5B
    61

    View Slide


  62. 1. Espresso
    2. Espresso
    3.

    4. " #!
    5. sleep()

    62

    View Slide

  63. IdlingResource ) )
    IdlingResource
    ⭕)

    ❌ E
    ( (

    63

    View Slide

  64. IdlingResource(2



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

    View Slide

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

    View Slide

  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());
    }

    View Slide

  67. UI Automator
    #
    p $ )uiDevice.wait()#
    p Until

    %"!&
    pfindObject()
    pfindObjects()
    pgone()
    phasObject()
    67
    uiDevice.wait('%",
    ()

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide


  71. pIdlingResource#*
    !
    p),"%'
    p (

    UI AutomatorAPI&+$
    71

    View Slide

  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

    View Slide

  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

    View Slide


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

    View Slide


  75. Espresso



    75


    View Slide