我如何单元测试(使用 JUnit 或 mockito)recyclerview 项目点击

How do I unit test (with JUnit or mockito) recyclerview item clicks

我目前正在尝试使用 junit 或 mockito 对 recyclerview addonitemclick listner 进行单元测试。这是我的代码:

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,模拟功能等。我对单元测试还很陌生,一直在网上查找一些教程,但很困惑。任何有关测试函数的分步教程或任何建议都会 help.Also,此函数中任何其他可能的单元可测试场景都会有所帮助。谢谢!

我可能会将您在 addOnItemTouchListener 中创建的匿名内部 class 提取到单独的 class。

然后我会为 onItemClick 方法编写相关的(单元)测试。

但这在很大程度上取决于您的应用程序的整体上下文以及您想要测试的确切内容。

关于单元测试与集成测试的讨论非常昂贵,而且对于什么是真正的单元测试也存在一些混淆和不同意见。

我建议开始阅读 Martin Fowler 的优秀系列文章中有关该主题的更多内容 - 例如https://martinfowler.com/bliki/UnitTest.html 还有另一篇关于一般测试替身的文章,它应该指导您是否要使用模拟或存根:https://martinfowler.com/articles/mocksArentStubs.html

在单元测试中,重要的是要有小的、可测试的代码块,我宁愿有 10 个方法具有单一的 resposinbility,而不是一个方法用于所有操作。

所有使用的输入都应作为参数传递给方法,然后测试在给定的输入下您是否会收到预期的输出。

不要测试您不拥有的东西 - 测试 View 的 onClick() 是 AOSP 工作的一部分。您可以测试您对 onClickListener 的反应。

您应该对处理逻辑的 class 进行测试。比在你的测试中你实例化这个 class 来测试它并模拟其他一切(通常好的方法是通过构造函数传递依赖关系)

示例:

所以如果你有像

这样的方法
goToDetailActivity(Photo photo){...}

我会把它包装在界面中,我们称之为 View。在 View 中,您还放置了您的逻辑必须调用的所有其他方法,并且与视图相关,例如与视图组件交互、导航等。 比你应该有你的逻辑 class,让我们称之为 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 你应该通过 activity/fragment (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 tutorial.