(Android) 同期が必要なテストを書く

##  IdlingResourceを使ったUIテスト
Espressoを使って，このようなテストを書きました．

1. リスト表示用のデータをサーバからとってくる
2. データをリストにセットする
3. リストをクリックする
4. 遷移先の詳細画面に，正しい情報が送れたか確認

このテストでは，
*「リストにデータがセットされたのを確認してから，クリック」*
という流れを書かなければいけません．

Espressoでは，テストするアクションのタイミングをコントロールできるよう，
`IdingResource`というものが用意してあります．

今回はその基本的な使い方について話します．

## IdingResrouceとは
`IdlingResrouce`とは，`android.support.test.espresso`に含まれるインターフェースで，
以下3つの抽象メソッドが定義されています．

1. `getName ()` : logなどで使うためのリソースの名前の定義
2. `isIdleNow ()` : テストするアクションをいつまで待たせるかの定義
3. `registerIdleTransitionCallback()` : コールバックオブジェクトの受け取り

「ある条件を満たしてから，テストアクションを行う」ことをEspressoに指示するためには，
*IdlingResourceを実装したクラスを作成して，Espressoに登録する* という手順を踏みます．

## IdlingResrouceの作成
今回は，*「リストにデータがセットされた」*という条件を次のように定義します．

```java
public boolean isItemLoaded(RecyclerView list) {
    return list.getAdapter()!=null && list.getAdapter().getItemCount() != 0;
}
```

上の条件をEspressoに伝えるためのIdlingResourceはこうなります．

```java:LoadingIdlingResource
private class LoadingIdlingResource implements IdlingResource {

    private ResourceCallback resourceCallback;
    private RecyclerView recyclerView;

    private LoadingIdlingResource(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
    }

    @Override
    public String getName() {
        return LoadingIdlingResource.class.getSimpleName();
    }

    @Override
    public boolean isIdleNow() {
        boolean idle = isItemLoaded(recyclerView);
        if (idle && resourceCallback != null) {
            resourceCallback.onTransitionToIdle();
        }
        return idle;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }

    private boolean isItemLoaded(RecyclerView list) {
        return list.getAdapter()!=null && list.getAdapter().getItemCount() != 0;
    }
}
```
同期のロジックを定義する`isIdleNow()`メソッドの定義が主な作業になります．
Espressoは，`LoadingIdlingResource.isIdleNow()`を呼んで`true`が返ってきたら，
次のテストアクションの実行に移ります．

## IdlingResourceを使ったテスト
作成した`LoadingIdlingResource `を使って，
*「リストにデータがセットされたのを確認してから，クリック」*
という流れを含むテストはこうなります．

```java:LoadingIdlingResrouceTest.java
public void testRecyclerView_click() {
    // Set up an ActivityMonitor
    Instrumentation.ActivityMonitor receiverActivityMonitor =
            getInstrumentation().addMonitor(ImageDetailActivity.class.getName(),null, false);

    // wait until list item is loaded
    IdlingResource idlingResource = new LoadingIdlingResource(mRecyclerView);
    Espresso.registerIdlingResources(idlingResource);

    // start DetailActivity by clicking RecyclerView item.
    Espresso.onView(ViewMatchers.withId(android.R.id.list)).perform(
            RecyclerViewActions.actionOnItemAtPosition(0, ViewActions.click()));

    // unregister idling resources
    Espresso.unregisterIdlingResources(idlingResource);

    // Remove the ActivityMonitor
    getInstrumentation().removeMonitor(receiverActivityMonitor);

    // assertion
    Activity activity = receiverActivityMonitor.waitForActivityWithTimeout(1000);
    Intent intent = activity.getIntent();
    String json = intent.getStringExtra(ImageDetailActivity.EXTRA_CONTENT);
    assertEquals(getExpected(), json);
}
````

##終わりに
このブログを参考に実装しました．分かりやすいです．
[Square Island: Espresso: Custom Idling Resource](http://blog.sqisland.com/2015/04/espresso-custom-idling-resource.html)

##参考URL
[EspressoSamples - android-test-kit - Advanced Espresso Samples](https://code.google.com/p/android-test-kit/wiki/EspressoSamples#Using_registerIdlingResource_to_synchronize_with_custom_resource)
[Square Island: Espresso: Custom Idling Resource](http://blog.sqisland.com/2015/04/espresso-custom-idling-resource.html)