LoginSignup
0
2

More than 3 years have passed since last update.

【Android】Espressoでの非同期テストでIdlingResourceを使用する

Posted at

Espressoで非同期テストのやり方を学んだのでメモ

IdlingResource

Espressoで非同期な処理を使用したUIテストを実施するときは、安易にsleepとか使用せずに、IdlingResourceを使うことが推奨されているようです。

IdlingResource自体はインタフェースなので、実際にはIdlingResourceを実装したクラスが必要になりますが、標準でいくつか用意されており今回は一番スタンダードなCountingIdlingResourceを使用します。

アクティブなタスクのカウンタを保持します。カウンタがゼロの場合、関連するリソースはアイドリング状態であると見なされます。この機能は、Semaphore の機能によく似ています。テスト中にアプリの非同期操作を管理するには、通常はこの実装で十分です。

アイドルリソース使用の流れ

アイドルリソース使用の流れは以下になります。

  1. テスト前にアイドルリソースを登録する
  2. 非同期処理開始前にアイドルリソースがビジーであることをセットする
  3. 非同期処理完了時点でアイドルリソースがアイドルであることをセットする
  4. テスト終了後にアイドルリソースを登録解除する。

一般的には、1は@Beforeで実施し、4は@Afterで実施するようです。

テスト設計

今回やりたいテストの流れについて設計します。

Untitled.png

「ProgressBar」「include」「WebView」の各コンポーネントが初期表示時は「Progress」のみ表示されていて、ページ読み込み完了し、Webページが表示できる直前で「WebView」のみ表示されるようになるよね?といった部分を確認するテストになります。

今回は、このうち「ページ読み込み完了」という部分が非同期でいつ完了になるのかわかりません。よって、アイドルリソースを使って「読み込み完了したよ!」ということをEspressoに教えてあげます。

テスト実装

設計に基づいて実装をおこないます。

まず、テスト対象のフラグメントに手を加えます。

public class TosViewFragment extends Fragment implements AppWebViewClientCallback {

    @VisibleForTesting
    private final CountingIdlingResource countingIdlingResource
            = new CountingIdlingResource("WebViewIdlingResource");

    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
    public IdlingResource getCountingIdlingResource() {
        return countingIdlingResource;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater,
                             @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        // ビジー状態
        countingIdlingResource.increment();
        // 省略
        // Viewの返却
        return binding.getRoot();
    }

    @Override
    public void onPageCommitVisibleHandle(boolean isOk) {
        // プログレスバーを非表示にする
        binding.scTovPbProgressBar.setVisibility(View.GONE);
        if (isOk) { // ページ表示可
            // Webページを表示する
            binding.scTovWvTos.setVisibility(View.VISIBLE);
        } else {
            // Sorryページを表示する
            binding.scTovInSorry.setVisibility(View.VISIBLE);
        }
        // アイドル状態
        countingIdlingResource.decrement();
    }
}

👉 CountingIdlingResourceのインスタンスを対象のフラグメントに生成します。(ゲッターとかはテストクラスからしか呼ばれないので@VisibleForTesting(otherwise = VisibleForTesting.NONE)をつけています。)

インスタンスを生成するときに、リソース名を引数にします。

つぎに、onCreateViewでビジー状態にしています。CountingIdlingResourceは内部でカウンタを持っており、ゼロになるとアイドル状態とみなされるので#incrementでカウンタをインクリメント(つまり、ビジー状態)にします。

最後に、onPageCommitVisibleHandleWebViewClient#onPageCommitVisibleが呼ばれた時のコールバック処理)の最後で#decrementしてあげて差し引き0(つまりアイドル状態)にしてあげます。

次にテストクラスに手を加えます。私はページオブジェクトが好きなので、ページオブジェクトで実装しています。

@LargeTest
@RunWith(AndroidJUnit4.class)
public class ScT_ScTov {
    public ActivityScenario<MainActivity> scenario;
    private IdlingResource idlingResource;
    @Before
    public void setUp() {
        // アクティビティの起動
        scenario = ActivityScenario.launch(MainActivity.class);
    }
    @After
    public void tearDown() {
        // アイドルリソースの登録解除
        IdlingRegistry.getInstance().unregister(idlingResource);
    }
    @Test
    public void ScreenTest_ScTov() {
        startScreenTest()
                .isNotDisplayedScTovWbTos()         // WebViewが表示されていないこと
                .isDisplayedScTovPbProgressBar()    // ProgressBarが表示されていること
                .isNotDisplayedScTovInSorry();      // includeが表示されていないこと
                registerIdlingResource()            // アイドルリソースを登録
                .isDisplayedScTovWbTos()            // WebViewが表示されていること
                .isNotDisplayedScTovPbProgressBar() // ProgressBarが表示されていないこと
                .isNotDisplayedScTovInSorry()       // includeが表示されていること
        ;
    }
    @NonNull
    private ScTovPO startScreenTest() {
        return new ScManPO()
                .clickHamburgerMenu()       // ハンバーガーメニュー_押下
                .clickTosViewMenuItem();    // 利用規約・免責事項メニューアイテム_押下
    }
    private ScTovPO registerIdlingResource() {
        scenario.onActivity(activity -> {
            // TosViewFragment取得
            TosViewFragment tosViewFragment =
                    (TosViewFragment) Objects.requireNonNull(activity
                            .getSupportFragmentManager()
                            .findFragmentById(R.id.sc_man_fc_nav_host))
                    .getChildFragmentManager()
                    .getFragments()
                    .get(0);
            // アイドルリソース取得
            idlingResource = tosViewFragment.getCountingIdlingResource();
            // アイドルリソースを登録
            IdlingRegistry.getInstance().register(idlingResource);
        });
        return new ScTovPO();
    }
}

👉 今回色々試してみましたが、上記のような形で成功しました。

処理の流れは以下の通りです。

  1. setUpでアクティビティを起動
  2. ScreenTest_ScTovでUIテスト開始
  3. startScreenTestでアクティビティから対象画面への遷移操作を実施
  4. ScreenTest_ScTovの途中で「アイドルリソースの登録」を行う。
  5. アイドルリソースをフラグメントから取得し登録する。
  6. テスト終了後、tearDownでアイドルリソースの登録解除

4,5は#registerIdlingResourceでやっています。アクティビティからフラグメントを取得し、ゲッターを介して取得したものをレジストしています。

テスト評価

Espressoを使ってUIテストを実施します。

Testing started at 10:04 PM ...
04/12 22:04:00: Launching 'ScreenTest_ScTov()' on Pixel 2 API 28 (SmartPhone).
App restart successful without re-installing the following APK(s): androidx.test.orchestrator, androidx.test.services
Running tests

無事テストが成功したことがわかります。

IdlingResourceを使えば、Espressoでの非同期テストも簡単に評価することができました。

めでたしめでたし。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2