使用Robolectric进行Android单元测试

单元测试在很多人认为是一件很麻烦的事情,然而,某一天,读了小创关于Android单元测试的一篇文章,让我鼓起了对于这个麻烦事情的勇气。

I’ve found that writing good tests greatly speeds my programming, even if I’m not refactoring. This was a surprise for me, and it is counterintuitive for many programmers…
–Martin Fowler 《Refactoring: Improving the Design of Existing Code》
<

你没有看错,这位大师竟然说能提高你的开发进度!我读书不多,不要骗我!

Martin Fowler在《重构》里面还解释了为什么单元测试可以节省时间,大意是我们写程序的时候,其实大部分时间不是花在写代码上面,而是花在debug上面,是花在找出问题到底出在哪上面,而单元测试可以最快的发现你的新代码哪里不work,这样你就可以很快的定位到问题所在,然后给以及时的解决,这也可以在很大程度上防止regression(相信QE和QA们一定很喜欢哈哈。。。),这也是个大部分程序员和测试都很痛恨的问题。

既然能提高开发速度,那我就要研究研究了!

为什么要做单元测试?

我们在写单元测试时,为了测试某个功能或某个api,首先得调用相关的代码,因此我们留下来的便是一段如何调用的代码。这些代码的价值在于为以后接手维护/重构/优化功能的人,留下一份程序猿最愿意去阅读的文档。

为什么Android 的单元测试不好做?

我们知道安卓的app需要运行在delvik上面,我们开发Android app是在JVM上面,在开发之前我们需要下载各个API-level的SDK的,下载的每个SDK都有一个android.jar的包,这些可以在你的android_sdk_home/platforms/下面看到。当我们开发一个项目的时候,我们需要指定一个API-level,其实就是将对应的android.jar 加到这个项目的build path里面去。这样我们的项目就可以编译打包了。然而现在的问题是,我们的代码必须运行在emulator或者是device上面,说白了,就是我们的IDE和SDK只提供了开发和编译一个项目的环境,并没有提供运行这个项目的环境,原因是因为android.jar里面的class实现是不完整的,它们只是一些stub,如果你打开android.jar下面的代码去看看,你会发现所有的方法都只有一行实现:
throw RuntimeException(“stub!!”);
而运行unit test,说白了还是个运行的过程,所以如果你的unit test代码里面有android相关的代码的话,那运行的时候将会抛出RuntimeException(“stub!!”)。为了解决这个问题,现在业界提出了很多不同的程序架构,比如MVP、MVVM等等,这些架构的优势之一,就是将其中一层抽出来,变成pure Java实现,这样做unit testing就不会遇到上面这个问题了,因为其中没有android相关的代码。
好奇的童鞋可能会问了,既然android.jar的实现是不完整的,那为什么我们可以编译这个项目呢?那是因为编译代码的过程并没有真正的运行这些代码,它只会检查你的接口有没有定义,以及其他的一些语法是不是正确。举个简单的例子:

public class Test {
    public static void main(String[] argv) {
        testMethod();
    }
    public static void testMethod() {
        throw RuntimeException("stub!!”);
    }
}

上面的代码你同样可以编译通过,但你运行的时候,就会抛出异常RuntimeException(“stub!!”)。当我们的项目运行在emulator或者是device上面的时候,android.jar被替换成了emulator或者是device上面的系统的实现,那上面的实现是真正实现了那些方法的,所以运行起来没有问题。
话说回来,MVP、MVVM这些架构模式虽然解决了部分问题,可以测试项目中不含android相关的类的代码,然而一个项目中还是有很大部分是android相关的代码的,所以上面那种解决方案,其实是放弃了其中一大块代码的unit
test。 当然,话说回来,android还是提供了他自己的testing
framework,叫instrumentation,但是这套框架还是绕不开刚刚提到的问题,他们必须跑在emulator或者是device上面。这是个很慢的过程,因为要打包、dexing、上传到机器、运行起来界面。。。这个相信大家都有体会,尤其是项目大了以后,运行一次甚至需要一两分钟,项目小的话至少也要十几秒或几十秒。以这个速度是没有办法做unit
test的。 那么怎么样即可以给android相关的代码做测试,又可以很快的运行这些测试呢?

写好单元测试的意义

我们经常讲“前人种树,后人乘凉”,然而在软件开发中,往往呈现出来的却是截然相反的景象,我们在绩效和指标的驱使下,主动或被动的留下来大量坏味道的代码,在短时间内顺利的完成项目,此后却花了数倍于开发的时间来维护此项目,可谓“前人砍树,后人遭殃”,讽刺的是,砍树的人往往因为优秀的绩效,此时已经步步高升,而遭殃的往往是意气风发,步入职场的年轻人,如此不断轮回。所以,为了打破轮回,从一点一滴做起吧,“树”的种类众多,作为任意一名普通的软件工程师,种好单元测试这棵树,便是撒下一片荫凉。

闲话说了这么多,下面介绍神器RoboLectric,此框架使用Android.jar,所以可以和使用测试驱动Android应用,测试只需要几秒就可以在JVM中完成,减少了devlik麻烦的工作量。
官网为:robolectric

以下以Android Studio 为例

1,环境搭建:

Gradle配置: 在build.gradle中配置如下依赖关系

dependencies {
testCompile 'junit:junit:4.12'
testCompile 'org.robolectric:robolectric:3.0'
}

通过注解配置TestRunner

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class)
public class SampleActivityTest {

}

请注意,您必须指定 constants 字段指向由编译系统产生的 buildconfig.class。robolectric使用 constants 计算使用的工具类建设项目当输出路径。没有这些配置,robolectric将无法找到您的资源或资产

在官方教程中,如果build with android studio2.0以上,可以不用设置TestArtfact ,因为编译器会默认设置,对于以下,可以按照 File Menu -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> Experimental.进行相关设置。

*这里要特别说明一下:假如你使用linux或者Mac电脑,需要设置一下默认JunitTest配置。Android Studio默认不设置工作目录的模块测试。可以通过编辑运行配置完成,默认-> JUnit和改变工作目录的价值module_dir美元美元。

这里写图片描述

官方对于Roboletric有一个比较比较成熟的案例在github上面,地址如下DEMO

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值