使用Robolectric做Android单元测试

首先谈谈,为什么要进行单元测试呢?理论上说,多了单元测试这一环节意味着要付出额外时间,如果开发周期不变,那么编写代码的时间必须相应缩短。这岂不是作为程序猿的我们自己给自己找麻烦,吃力不讨好吗?讲真的,我一开始也有这种想法,怀着半信半疑的态度来做单元测试。后来慢慢地发现,单元测试过程帮助自己找出考虑不周或者意料之外的bug,即时修复。这样一来,去到测试人员手中的app瞬间变得高大上,bug出现率有所降低,减少了二次测试验证所浪费的时间。那么,做单元测试的必要性还是存在的,它可以帮助我们快速定位bug,从而修复bug,提高代码质量。曾经有一次,我无意间看到Jake Wharton这位Android界大神都做单元测试,而且是几乎每个类都进行细节测试。我们有什么理由不进行单元测试呢?目前有AndroidJUnit4、Dagger、Robolectric、Mockito这四个测试框架,现在选择Robolectric来介绍下。

1、在Android Studio的Gradle环境测试

Robolectric相关依赖:

testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.robolectric:robolectric-annotations:3.0'
testCompile 'org.robolectric:shadows-multidex:3.0'
testCompile 'org.robolectric:shadows-support-v4:3.0'

另外是测试常用的Juint依赖,现在是出到4.12版本:

testCompile 'junit:junit:4.12'

2、在Maven环境中测试

需要在pom.xml文件中添加以下依赖:

<dependency>
   <groupId>org.robolectric</groupId>
   <artifactId>robolectric</artifactId>
   <version>3.1.4</version>
   <scope>test</scope>
</dependency>

3、在Eclipse环境测试
需要安装m2e-android插件,点击project -> Run as -> JUnit Test, 然后选择Eclipse JUnit,就可以运行起来。

4、Linux和Mac平台注意要点
打开Edit Configuration,选择Defaults->JUint,修改Working directory为自己指定的工作目录,如下图:


开始工作,需要在src目录下创建test文件夹,与main同级,如下图所示:


接下来看下Robolectric基本测试类:

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

    @Before
    public void setUp(){

    }

    @Test
    public void testSomeThing(){

    }
    
    @After
    public void tearDown(){
        
    }
}

其中,@RunWith指定在RobolectricGradleTestRunner类运行,@Config指定BuildConfig类作为constants常数,@Before方法里执行初始化操作,@Test执行真正测试,@After则是测试完毕的后续工作。@Before和@After不是非必须的。但是,如果没有添加@Config指定BuildConfig类作为constants常数,会抛出如下错误提示:


值得注意的是,目前Robolectric只支持API21以下版本。如果你在gradle配置targetSdkVersion大于21,会抛出如下错误:


初始化setUp方法里,可以是获取application实例,启动activity,根据id绑定控件等操作。

//获得application实例
application = ShadowApplication.getInstance();
//启动activity
activity = Robolectric.setupActivity(ToastActivity.class);
//断言application与activity不为null
assertNotNull(application);
assertNotNull(activity);
//通过activity找到对应id的控件
btn_toast = (Button) activity.findViewById(R.id.btn_toast);
et_username = (EditText) activity.findViewById(R.id.et_username);

这里的ShadowApplication是shadow for android.os.application,也就是说真实对象是application。而Robolectric.setupActivity方法其实是通过反射得到真正类,并完成初始化操作:

public static <T extends Activity> T setupActivity(Class<T> activityClass) {
    return ActivityController.of(shadowsAdapter, activityClass).setup().get();
}
在setup方法里,完成了onCreate、onStart、onResume等activity生命周期的调用。
public ActivityController<T> setup() {
    return create().start().postCreate(null).resume().visible();
}
下面模拟登陆成功的测试, 其中指定正确userName为hello,使用assertThat进行断言下一个activity为非null。

@Test
public void success(){
    et_username.setText("hello");
    btn_toast.performClick();
    assertThat("The Next Activity Should Start", application.getNextStartedActivity(), is(notNullValue()));
    Log.e("123456", "Hello, Robolectric!");
}

测试是pass了,但是并没有log打印出来。为什么呢?难道是Robolectric偷工减料,跳过log打印的步骤了?不是这样的,真正原因是Robolectric跑单元测试是在jvm环境运行,而log来自android.util。其实Robolectric也考虑到这边,只需要添加一行代码便可实现转换,让你的log正常输出。

//打印log日志
ShadowLog.stream = System.out;

果然,Robolectric并没有欺骗我们感情,期待的log终于出现了。


好了,Robolectric做单元测试的基本用法已经介绍完毕。希望本文可以帮助大家开启单元测试之旅,尽情享受find bug & fix bug的乐趣。如果有什么问题,可以相互交流,为尽可能扼杀bug于摇篮,防bug于未然,提高代码质量,争取做一名更加出色的码农而奋斗。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐福记456

您的鼓励和肯定是我创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值