Android 单元测试实战(1)—— 调研与选型

单元测试搞了一段时间,发现网上不少关于单元测试的文章都是讲了概念,讲了框架的使用,但对于一个实际的项目的操做,由于项目的复杂性,框架的稳定性等,确每每没法进行。本篇博客从实际出发,基于实际的项目总结而出。java

本系列文章不会涉及到单元测试的概念,以及它的各类现实意义。仅从实现入手,关于它的优劣不作分析。android

单元测试系列会分为三篇博客:架构

  • Android单元测试调研与选型
  • 基于Powermock的Android单元测试经常使用方法指南
  • 基于Cobertra&sonarqube的单元测试覆盖率统计

调研与选型

Google官方文提供了单元测试的支持。在建立项目的时候会默认建立testandroidTest目录。分别是单元测试和集成测试。单元测试是对方法的测试,粒度较小,无需运行在真机上。集成测试须要每次运行须要跑在真机上,粒度较大,运行时间较长,并且不利于一些自动化的工做。app

本系列的核心从单元测试入手,以自动化为目的。框架

单元测试一个绕不开的话题就是对于android.jar的问题。因为android.*的官方类,在运行单元测试的时候,只有方法的声明,内部的全部方法都会throw new RuntimeException('Stub')。那么一旦有调用官方类的地方,好比ViewIntentActivity等,就会报错,致使单元测试没法执行。jvm

一种常见的解决方式是经过架构来解决,将一些代码逻辑和官方类解耦,好比MVP,对Presenter作测试,由于大部分逻辑都在Presenter,因此还ok。ide

可是不幸的,通常搞单元测试的时候,很难是从一个新项目入手的,架构很难变更,一旦修改架构,影响范围比较广。单元测试

而咱们的项目就是这样的,全部的逻辑都在Activity中编写,一旦测试逻辑就确定绕不开官方类。测试

官方文档上提到了单元测试的两种解决方案。spa

一种是经过Mock来解决,及将官方类的调用方法给代理一下,不会实际的调用官方类的相关方法。

另一种是Robolectric,该框架经过在jvm上模拟android虚拟机,以单元测试的方式来完成集成测试。

Robolectric(放弃)

由于最终没有使用这个框架,因此先介绍一下这个框架。

该框架至关因而搭建了一个android虚拟机,其运行单元测试的时候,实质是运行了一个app。那么其作测试的逻辑更倾向于appium等的UI测试,查询一个控件,模拟点击,验证逻辑。

由于其模拟的虚拟机,那么他对官方的方法作了扩展,提供了一些列的ShadowXXX类,便于作验证和模拟。好比获取当前弹出的dialog,最后一个弹出的toast等等。

调研的时候,该框架最新版本为4.3,而且从4.0开始,已经开始和官方的androidx.test下测试库进行兼容。能够经过官方espresso完成一些列操做。那么一套代码,技能在控制台运行,又能在模拟器上运行,想一想仍是挺美好的。

可是 !!!

由于咱们的项目都是在Activity里面写的,一些业务逻辑都是使用私有方法,那么相对私有方法作验证,经过查询控件和经过UI的展现来验证逻辑的正确与否,十分的复杂。

举个例子:

@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.skip:
                startNextActivity(false);
                break;
            case R.id.splash_image:
                startNextActivity(true);
                break;
            default:
                break;
        }
    }
    
    private synchronized void startNextActivity(boolean isClick) {}
复制代码

我想验证不一样的View点击,调用的startNextActivity的参数是否正确。若是从UI上验证十分的复杂,并且万一startNextActivity的内部逻辑有问题,或者比较巧妙,那么UI的验证也不必定准确。

有方法解决吗??确定有,即是Mock,这个后面会说。

可以Mock私有方法的经常使用的是Powermock,可是!!!!!

该框架和Robolectric存在着各类兼容问题,在我头发掉了一地以后,也没有解决。

因此放弃了!!!!注意是放弃了Robolectric!!!!

Mock (使用)

mock是单元测试中经常使用的一种方式,经过对即将调用方法的修改,模拟调用方法的返回值等等,具体的概念百度上一大堆。我就再也不这里废话了。

官方文档上建议使用Mockito完成mock操做,可是该库不支持静态方法,私有方法,final等的mock

上面提到了Powermock,他提供更增强大的mock功能,并且它提供了Mockito的支持,使用上和Mockito基本上同样。

好比说,对于上面的代码,使用Powermock方法验证逻辑以下:

@Test
    public void onClickSkip() throws Exception {
        // mock activity, activity的全部方法都不会被执行
        LauncherActivity activity = PowerMockito.mock(LauncherActivity.class);
        // 指定activity的onClick不被`mock,调用真实的逻辑,以便进行单元测试
        PowerMockito.doCallRealMethod().when(activity, "onClick", ArgumentMatchers.any(View.class));

		 // Mock 官方类
        View view = PowerMockito.mock(View.class);
        // 指定getId的返回值
        PowerMockito.doReturn(R.id.skip).when(view, "getId");
		 // 调用测试的方法 
        activity.onClick(view);
     	 // 验证指定的方法和参数是否被滴啊用 
        PowerMockito.verifyPrivate(activity).invoke("startNextActivity", false);
    }

复制代码

代码注释很清楚,不在废话。

按照上面的思路,其实能够验证大部分的单元测试逻辑。

总结

综上所述,决定使用Powermock为基础,完成单元测试的编写。

相关文章
相关标签/搜索