14

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

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

4

2 回答 2

9

在单元测试中,拥有小的、可测试的代码块是很重要的,我宁愿拥有 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教程

于 2017-02-21T10:12:01.760 回答
4

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

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

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

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

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

于 2017-02-14T22:07:34.510 回答