web-dev-qa-db-ja.com

(JUnitまたはmockitoを使用して)recyclerviewアイテムのクリックを単体テストするにはどうすればよいですか?

私は現在、junitまたはmockitoのいずれかを使用してrecyclerview addonitemclicklistnerの単体テストを試みています。これが私のコードです:

private void mypicadapter(TreeMap<Integer, List<Photos>> photosMap) {
    List<PhotoListItem> mItems = new ArrayList<>();

    for (Integer albumId : photosMap.keySet()) {
        ListHeader header = new ListHeader();
        header.setAlbumId(albumId);
        mItems.add(header);
        for (Photos photo : photosMap.get(albumId)) {
            mItems.add(photo);
        }


        pAdapter = new PhotoViewerListAdapter(MainActivity.this, mItems);
        mRecyclerView.setAdapter(pAdapter);
        //  set 5 photos per row if List item type --> header , else fill row with header.
        GridLayoutManager layoutManager = new GridLayoutManager(this, 5);
        layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                if (mRecyclerView.getAdapter().getItemViewType(position) == PhotoListItem.HEADER_TYPE)
                    // return the number of columns so the group header takes a whole row
                    return 5;
                // normal child item takes up 1 cell
                return 1;
            }
        });
        mRecyclerView.setLayoutManager(layoutManager);
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.addOnItemTouchListener(new PhotoItemClickListener(MainActivity.this,
                new PhotoItemClickListener.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int position) {
                        if (pAdapter.getItemViewType(position) == PhotoListItem.HEADER_TYPE) return;

                        Photos photo = pAdapter.getItem(position);
                        Intent intent = new Intent(MainActivity.this, DetailViewActivity.class);
                        intent.putExtra(PHOTO_DETAILS, photo);
                        ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                                MainActivity.this,

                                new Pair<>(view.findViewById(R.id.photoItem),
                                        getString(R.string.transition_name_photo))
                        );
                        ActivityCompat.startActivity(MainActivity.this, intent, options.toBundle());
                    }
                }));
    }

ユニットテストを行う方法はありますか:addOnItemTouchListenerまたはOnItemClickListener/onitemclick、機能のモックなど。私はユニットテストにかなり慣れておらず、いくつかのチュートリアルをオンラインで調べていて、かなり混乱しています。関数をテストするためのステップバイステップのチュートリアルや提案があれば役立ちます。また、この関数で可能な他のユニットテスト可能なシナリオも役立ちます。ありがとう!

14

単体テストでは、テスト可能な小さなコードのチャンクを用意することが重要です。すべてのアクションに対して1つのメソッドではなく、単一の責任を持つ10のメソッドが必要です。

使用されるすべての入力は、指定された入力で期待される出力を受け取るかどうかをテストするよりも、メソッドへのパラメーターとして提供する必要があります。

所有していないものをテストしないでください。ViewのonClick()のテストはAOSPジョブの一部です。 onClickListenerにどのように反応するかをテストできます。

ロジックを処理するテスト対象のクラスが必要です。テストでは、このクラスをインスタンス化してテストし、他のすべてをモックします(通常、コンストラクターを介して依存関係を渡すのが良い方法です)

例:

だからあなたがのような方法を持っているならそのように

_goToDetailActivity(Photo photo){...}
_

インターフェイスでラップし、Viewと呼びましょう。 Viewには、ロジックが呼び出す必要があり、ビューコンポーネントやナビゲーションなどとの対話など、ビューに関連する他のすべてのメソッドも配置します。ロジッククラスが必要な場合よりも、Presenterと呼びましょう。

_public class Presenter {
Presenter(View:view) {
    this.view = view;
}

public void onPhotoClicked(Photo:photo) {
    if (shouldDetailScreenBeOpened())
        view.goToDetailActivity(Photo photo);
    else view.showError("error");
}

private boolean shouldDetailScreenBeOpened() {
    // do caclualtions here
    ...}
}
_

私はアダプターをビューの一部として扱っているので、実際のロジックはありません。したがって、クリックをPresenterに渡すには、アクティビティ/フラグメント(View実装)を介してPresenterに渡す必要があります(誰かがRxJavaを楽しんでいる場合は、RxBindingライブラリを使用できます)。 onPhotoClicked(photo)メソッドを呼び出します。

そして、テストでは、必要なものをモックする必要があります(そしてテストの対象ではありません):

_ View view= Mockito.mock(View.class);

 Presenter tested = Presenter(view); 

 Photo validPhoto = Mockitio.mock(Photo.class);
 Mockito.when(validPhoto.getUrl()).thanReturn("image.com")

 //call method which will be triggered on item click
 tested.onPhotoClicked(validPhoto)

 //Check if method was invoked with our object
 Mockito.verify(view).goToDetailActivity(validPhoto);

 //Check also not so happy path
 Photo invalidPhoto = Mockitio.mock(Photo.class);
 Mockito.when(invalidPhoto.getUrl()).thanReturn(null)

 //call method which will be triggered on item click
 tested.onPhotoClicked(invalidPhoto)
 Mockito.verify(view,never()).goToDetailActivity(invalidPhoto);
 Mockito.verify(view).showError("error")
_

良い凝視点 vogella mokcitoチュートリアル

10

おそらく、addOnItemTouchListenerで作成している匿名の内部クラスを別のクラスに抽出します。

次に、onItemClickメソッドに関連する(単体)テストを記述します。

ただし、これはアプリケーションの全体的なコンテキストと、テストする内容によって大きく異なります。

単体テストと統合テストについての議論はかなり費用がかかり、実際の単体テストとは何かについて混乱や意見の相違もあります。

マーティン・ファウラーの優れた一連の記事から、このトピックについてもっと読み始めることをお勧めします。 https://martinfowler.com/bliki/UnitTest.html 一般的なテストダブルに関する別の記事もあります。これは、モックとスタブのどちらを使用するかについてガイドする必要があります。 https://martinfowler.com/articles/mocksArentStubs.html

3
gsaslis