Android单元测试<中>(Android官方MVP架构示例项目解析)

解读Android官方MVP项目单元测试分析得很到位
——本篇以此为基础,有所补充

本文通过分析Android官方MVP项目中最基础的todo-mvp/示例项目,来归纳如何测试。(本篇不会介绍此Demo的逻辑、源码结构,请阅读代码之后再读此文)

一、测试Presenter层

这里只说主页面的TasksPresenter中的loadTasks方法(获取所有数据)
这里写图片描述

从时序图上看,loadTask执行的逻辑是:

  • 1.调用View层开启进度条
  • 2.从Model层获取待办任务列表
  • 3.Model层以回调函数的形式返回数据
  • 4.调用View层关闭进度条
  • 5.调用View层显示任务列表。

这5个步骤里,每个步骤的逻辑是否准确是View层和Model层该测试的事情,对于Presenter层来讲,他的测试任务是确保这5个步骤如期调用。为了达成此目的,我们会采用Mockito.verify()的api进行测试,这个测试类是TasksPresenterTest,代码如下:

@Test
public void loadAllTasksFromRepositoryAndLoadIntoView() {
        // 塞选当前展示数据是All视图状态
        mTasksPresenter.setFiltering(TasksFilterType.ALL_TASKS);
        // 第0步:加载数据
        mTasksPresenter.loadTasks(true);

        // Callback is captured and invoked with stubbed tasks
        // 第2步:验证mTasksRepository调用了getTasks,并将真实的LoadTasksCallback捕获到ArgumentCaptor<T>中
        verify(mTasksRepository).getTasks(mLoadTasksCallbackCaptor.capture());
        // 第3步:调用捕获的回调参数,主动调用onTasksLoaded,并传入有效值
        mLoadTasksCallbackCaptor.getValue().onTasksLoaded(TASKS);

        // Then progress indicator is shown
        // 验证第1步:验证走了现实loading方法,进度条显示
        verify(mTasksView).setLoadingIndicator(true);
        // Then progress indicator is hidden and all tasks are shown in UI
        // 验证第4步:验证隐藏了loading,进度条关闭
        verify(mTasksView).setLoadingIndicator(false);

        // 这个也是参数捕获器
        ArgumentCaptor<List> showTasksArgumentCaptor = ArgumentCaptor.forClass(List.class);
        // 验证第5步:View层显示待办任务列表,验证数据为之前预设的3条
        verify(mTasksView).showTasks(showTasksArgumentCaptor.capture());
        assertTrue(showTasksArgumentCaptor.getValue().size() == 3);
    }

总结:让Presenter充当个合格的皮条客,去调用其他两层的逻辑,在假设其他两层代码逻辑都是正确的前提下,做一些mock测试,尽可能覆盖所有逻辑路径。

二、测试View层

知识补充:

1. JUnit4中的@Rule完全解析

  • @Rule是JUnit4的新特性。利用@Rule我们可以扩展JUnit的功能,在执行case的时候加入测试者特有的操作,而不影响原有的case代码:减小了特有操作和case原逻辑的耦合。
  • TestRule是一个工厂方法模式中的Creator角色——声明工厂方法。
  • 由于工厂方法apply有参数base,因而TestRule将创建装饰模式中的装饰对象(Statement)。
  • 这里写图片描述

AndroidJUnit4中的ActivityTestRule也是实现了TestRule,作用:

  • 执行每个@Test方法之前打开相应的Activity
startIntent = new Intent(Intent.ACTION_MAIN);
startIntent.setClassName(targetPackage,mActivityClass.getName());
startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityClass.cast(mInstrumentation.startActivitySync(startIntent));
  • 扩展了一些方法,在Activity创建之前以及之后的相应方法
beforeActivityLaunched();
afterActivityLaunched();
  • 确保模拟的操作均在UI线程中执行
getInstrumentation().runOnMainSync(new Runnable() {
                public void run() {
                    try {
                        mBase.evaluate();
                    } catch (Throwable throwable) {
                        exceptionRef.set(throwable);
                    }
                }
            });

2.Espresso的测试等待异步结果机制

开发者面临的挑战之一,是在编写UI测试时需要等待异步计算或I/O操作完成。这里使用Espresso测试框架来解决这个问题和学到的一些技巧。

Idling Resource

Espresso引入了Idling Resource的概念,它是一个简单的接口:

它代表了被测应用程序的资源,这个资源在测试执行时可以在后台异步工作。

接口定义了三个方法:

  • getName():必须返回代表idling resource的非空字符串;
  • isIdleNow():返回当前idlingresource的idle状态。如果返回true,onTransitionToIdle()上注册的ResourceCallback必须必须在之前已经调用;
  • registerIdleTransitionCallback(IdlingResource.ResourceCallback callback):通常此方法用于存储对回调的引用来通知idle状态的变化。
  • 创建完自定义的idling resource后,需要通过调用Espresso.registerIdlingResource(webViewIdlingResource)与Espresso注册。

总结:Idling Resource其实是Espresso的一个用来等待异步耗时操作的一个指示器(是否是空闲状态)、通知器(转为空闲状态时,通知Espresso)

  • 先通过Espresso.registerIdlingResource在测试中将Idling Resource注册给Espresso
  • 异步耗时操作之前调节自定义的Idling Resource中的参数,使得现在isIdleNow()返回false(即标记为非空闲状态)
  • 异步耗时操作结束时,修改自定义的Idling Resource中的参数,使得现在isIdleNow()返回true(即标记为空闲状态)
  • 再调用registerIdleTransitionCallback(ResourceCallback resourceCallback),Espresso回调的ResourceCallback参数的onTransitionToIdle()方法,告知Espresso状态转到空闲

开启View层的测试

其实View层的测试是从QA角度出发,以测试用例为基本单位,做得好甚至可以用Suite结合起来,做到自动化测试,Google下的绝大多数App其实都运用了这个测试框架进行UI测试。

  • 第一步:测试哪个页面
@Rule
public ActivityTestRule<TasksActivity> mTasksActivityTestRule =
            new ActivityTestRule<TasksActivity>(TasksActivity.class);
  • 第二步:给Espresso注册Idling Resource
@Before
public void registerIdlingResource() {
        Espresso.registerIdlingResources(
                mTasksActivityTestRule.getActivity().getCountingIdlingResource());
    }

实际代码中自定义的Idling Resource需要在特定地方进行标记

  • 第三部开始测试

站在QA的角度,我们想要验证待办任务列表时候,会设计以下的测试用例:
这里写图片描述

请点击此入口进入这部分内容

总结:这一层的测试站在用户的角度,进行黑盒测试,在功能层面测试输入(用户操作)输出(用户得到的界面反馈),其实已经粗糙的覆盖了M和P的逻辑了,做好其实就是自动化测试了。

三、Model层的测试

这部分与UI无关,有部分Model层的逻辑,网络、数据库、本地文件等部分需要Android环境,所以:
- Model纯逻辑部分:Local Test > JUnit4 + Mockito + Hamcrest > 存放test下
- Model需要Android环境部分:AndroidJUnit4 > 存放 androidTest 下

请点击此入口进入这部分内容

这张图不得不盗用下
这里写图片描述

Model层注意(Fake、门面模式):

  • 网络请求:不测试真实的网络请求,但提供了Fake供其他层调用测试,其实是面相接口的空实现。
  • 封装的门面类:决定了数据的来源和去向是来自于本地数据库 or 网络 or 内存,此为真正对其他层暴露的Model类。此类不做数据准确性的验证,只做mock测试,验证覆盖路径。UT选型Junit+Mockito,代码存放于test中。

MVP模式少了Dragger2总不那么完美,下一篇结合Dragger2的单元测试会有点久

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值