Robolectric 探索之路
layout: post
title: Roboletric探索之路,从抗拒到依赖
description: Roboletric Android Unit Testing
category: blog
我为什么以前抗拒Android Unit Testing
- 1、懒,人类最大的天敌;
- 2、不是不知道什么是单元测试,只是需求太多了,哪有时间~;
- 3、需要学习单元测试的语言或者框架,不熟悉,所以从没尝试过;
- 4、没见到单元测试的好处,一想到要花时间就望而却步;
- 5、至少只是我个人之前的感受,我相信有很多的程序猿同胞们都跟我有类似的感受;
既然抗拒,为什么现在要尝试Android Unit Testing呢
大势所趋,bug量的增多不得不让我们提高代码的质量,不是我们完不成功能,只是我们验证功能的成本实在太高,随着工程的复杂度的增加,run一次模拟器或者真机,在window上的花费至少是一分钟以上,甚至三四分钟,所以有些人偷懒,包括我,有时候把那些看上去“没有问题的代码”提交到了主干上,随之产生了bug,然后进入修复bug-》run-》修复bug->run;花费了更多的时间和资源;
我们的燃眉之急是要尽快改善这个问题,从根源着手,就是【增强自测】
测试手段
现在是个讲究效率的时代,我们希望能够快速高效的验证我们的代码逻辑是否有问题,我们不希望验证一个简单的逻辑或者一个方法是否有效,是通过run一次模拟器或者整个工程实现的,这样花费的时间太长了,降低了开发效率;
- 1、所以我们要解决的第一个痛点是,如何快速验证;
我们选择了Robolectric单元测试框架,原因有好几个,最大的原因是:
Robolectric
Test-Drive Your Android Code
Running tests on an Android emulator or device is slow! Building, deploying, and launching the app often takes a minute or more. That's no way to do TDD. There must be a better way.
Wouldn't it be nice to run your Android tests directly from inside your IDE? Perhaps you've tried, and been thwarted by the dreaded java.lang.RuntimeException: Stub!?
它不需要Run你的模拟器,直接在jvm上运行你的测试代码,能在几秒钟之内快速验证,通过体验之后,它确实非常高效,编写测试代码反而加速了开发效率。
具体的原理描述可参见:
Talk is cheap ,show me the code
环境配置
Android Stuido 1.5.1
junit:junit:4.12
Robolectric 3.0(不要用3.0-r3,有很多bug,踩了很多坑)
具体配置:
1、在app的build.gradle依赖添加如下:
testCompile 'junit:junit:4.12'
testCompile ’org.robolectric:robolectric:3.0’
2、在android studio左下角的Build Variants->TestArtifact,选择为Unit Tests;
3、编译;
编译通过之后就已经集成了Robolectic单元测试框架了。
我的第一个单元测试
先描述以下踩过的坑:
1.使用3.0-r3版本,原因是google搜到的例子是3.0-r3,傻傻的掉进了坑里;
找不到合并后的mainifest;ContextWraper为空,webview初始化异常,无法加载so库
2.使用了3.0版本后,依然无法加载so库,现象是启动application的时候如果调用so库会出现闪退,目前robolectic还不支持这个东西,作者在github的issue已经说明;
3.这一点导致无法robolectic无法直接集成到我们的主工程;为了不影响正常项目开发,我们建了一个AppTest的空项目,集成了Robolectic框架;将所有用例分类写在里边,各个模块要测试的时候,把依赖写入build.gradle即可;
第一单元测试
创建测试类
参数解释:
@RunWith(RobolectricGradleTestRunner.class);声明使用哪个Runner,使用GradleTestRunner会自动帮我们加载所需要的插件,一般我们配这个就可以;
@Config(constants = BuildConfig.class,sdk=18);配置测试项目的BuildConfig和sdk版本,BuildConfig是编译器自动生成;@Config还可以配置很多其他的熟悉,比如Mainfest,Applciation,assert资源等等,具体了解可参见
http://robolectric.org/configuring/;
extend TestCase:这个必须继承的类;
@Before是前置条件,也就是在执行@Test之前会执行的方法,这个很好理解,@After同理;
@Test具体的单元测试方法;
例子解释:
@Before
@Before
public void setUp() {
//获取当前运行环境的Context;
Context context = RuntimeEnvironment.application.getApplicationContext();
//初始化BeanManager
BeanManager.getUtilSaver().setContext(context);
//初始化日历模块
CalendarController.getInstance().init(context, new OnCalendarListener(){});
}
每次执行test的时候,Robolectic执行顺序是:
模拟启动执行你的application,
执行@Before
执行@Test,
执行@After
所以如果没有没有执行初始化逻辑,@test很有可能会失败;
或者appliation里调用了so或者初始化了webview,也会失败;
一般在@Before我们做的是初始化的工作和构造一些模拟数据的操作;
@Test
@Test
public void doTestMainUI(){
//启动AnalysisMainActivity,并获取activity对象
Activity activity = Robolectric.setupActivity(AnalysisMainActivity.class);
//获取里边的控件
RelativeLayout linearLayout = (RelativeLayout)activity.findViewById(R.id.calendarNodataLayout);
int visible = linearLayout.getVisibility();
//判断是否可见
assertEquals(visible, View.VISIBLE);
}
@Test
public void doTestCurrentIdentify(){
//获取当前身份,可以在setup设置身份
int value = CalendarController.getInstance().getIdentifyManager().getIdentifyModelValue();
//验证身份
assertEquals(value, IdentifyModel. NORMAL);
}
@Test
public void testDomain(){
List<HttpDnsModel> list = mHttpDnsCacheManager.getHttpDnsModels();
assertEquals(list.size(), 2);
//验证解析格式
String domain = mHttpDnsCacheManager.getDomian("http://api.myms.meiyou.com/configs");
assertEquals(domain,"api.myms.meiyou.com");
//验证解析格式
domain = mHttpDnsCacheManager.getDomian("https://api.myms.meiyou.com/configs");
assertEquals(domain, "api.myms.meiyou.com");
//验证命中缓存
HttpDnsModel model = mHttpDnsCacheManager.getFromCache("api.myms.meiyou.com/configs");
assertEquals(model!=null,true);
assertEquals(model.getIp(), "211.151.209.71");
//验证替换
String result = mHttpDnsCacheManager.replaceDomianToIp("http://api.myms.meiyou.com/configs", domain, model.getIp());
assertEquals("http://211.151.209.71/configs",result);
}
@Test
public void testGetPhoto(){
//记录器
final Transcript transcript = new Transcript();
//创建一个activity
Activity activity = new Activity() {
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//记录收到的结果
transcript.add("onActivityResult called with requestCode " + requestCode + ", resultCode " + resultCode + ", intent data " + data.getData());
}
};
//启动去相册选择相册
activity.startActivity(new Intent().setType("image/*"));
//获取影子类,模拟设置ActivityResult结果
Shadows.shadowOf(activity).receiveResult(new Intent().setType("image/*"), Activity.RESULT_OK,
new Intent().setData(Uri.parse("content:foo")));
//验证收到的结果
transcript.assertEventsSoFar("onActivityResult called with requestCode -1, resultCode -1, intent data content:foo");
}
强大的影子类:
影子类的详细了解;
影子类是对安卓原生api类的一种拓展,通俗的解释是增加了一些用于测试的很方便的方法;3.0有很多影子类,获取方法是:Shadows.shadowOf();
上边的 Shadows.shadowOf(activity).receiveResult的方法;
影子类还可以自定义,遇到比较复杂的功能或者需要的功能可能会很有用;
单元测试的例子
大量的测试例子都在github的源码里边,可以详细参照;
Robolectric Github 源码地址;
Done
QQ:452825089
mail:452825089@qq.com
wechat:ice3897315
blog:http://iceAnson.github.io
Robolectric 探索之路的更多相关文章
- Android 单元测试(junit、mockito、robolectric)
1.运用JUnit4 进行单元测试 首先在工程的 src 文件夹内创建 test 和 test/java 文件夹. 打开工程的 build.gradle(Module:app)文件,添加JUnit4依 ...
- Android studio下gradle Robolectric单元测试配置
android studio下gradle Robolectric单元测试配置 1.Robolectric Robolectric是一个基于junit之上的单元测试框架.它并不依赖于Android提供 ...
- Robolectric 配置
费了些工夫,已配好,按记录留记录 按官网操作http://robolectric.org/getting-started/ 1引包 testCompile "org.robolectric: ...
- 在Android Studio中用Gradle添加Robolectric
我们用Robolectric测试的话需要在gradle中进行配置,国内的详细教程太过简易,而且很多是低版本下的配置方案.所以经过本人的仔细摸索,找到了现在高版本中的配置方案,主要还是参考了官网的配置教 ...
- robolectric环境的搭建
最近在学习测试驱动开发(Test-Driven Development),测试驱动开始是极限编程的一种方式,提倡在真正编写代码之前先根据需求编写测试代码(当然这个测试代码是不可能通过的),然后根据测试 ...
- Extending Robolectric
Robolectric is a work in progress, and we welcome contributions from the community. We encourage dev ...
- Configuring Robolectric
There are numerous ways to customize how Robolectric behaves at runtime. Config Annotation The prima ...
- Robolectric Test-Drive Your Android Code
Running tests on an Android emulator or device is slow! Building, deploying, and launching the app o ...
- OpenCV探索之路(二十四)图像拼接和图像融合技术
图像拼接在实际的应用场景很广,比如无人机航拍,遥感图像等等,图像拼接是进一步做图像理解基础步骤,拼接效果的好坏直接影响接下来的工作,所以一个好的图像拼接算法非常重要. 再举一个身边的例子吧,你用你的手 ...
随机推荐
- 开发框架CIIP
github开源:企业级应用快速开发框架CIIP WEB+WIN+移动端 简介 CIIP是基于XAF开发的开源信息系统框架.CIIP最常见的应用场景是基于数据库的企业级应用程序,例如供应链系统,E ...
- oracle数据库删除数据Delete语句和Truncate语句的对比
oracle数据库删除数据Delete语句和Truncate语句的对比 当表中的数据不需要时,则应该删除该数据并释放所占用的空间,删除表中的数据可以使用Delete语句或者Truncate语句,下面分 ...
- Form Presonalization 表单个性化定义控制应用
1.1.1 表单个性化定义 Oracle EBS 11.5.10所增加的Form Presonalization功能,是在对Form不进行开发的前提下,用一些系统内置的触发器,按照所设定的控制规则 ...
- 十度好友问题(DFS经典应用)
问题: 在社交网络里(比如 LinkedIn),如果A和B是好友,B和C是好友,但是A和C不是好友,那么C是A的二度好友,给定一个社交网络的关系图,如何找到某一个人的所有十度好友.
- java 多线程学习笔记
这篇文章主要是个人的学习笔记,是以例子来驱动的,加深自己对多线程的理解. 一:实现多线程的两种方法 1.继承Thread class MyThread1 extends Thread{ public ...
- 07.15 first与first-child的区别
举例: $("ul li:first"); //选取第一个 <ul> 元素的第一个 <li> 元素 $("ul li:first-child&q ...
- 填充Z形二维数组
形如 1 3 4 10 2 5 9 11 6 8 12 15 7 13 14 16 的数组称谓Z形二维数组.填充这样的数组其实只要按照Z形进行行走填充即可,设置一个flag指示方向,行 ...
- SendMessage用法实例
转: http://blog.csdn.net/coolszy/article/details/5523700 SendMessage用法 windowsbuttonmenucommandlistc# ...
- JavaScript之向文档中添加元素和内容的方法
一.非DOM方法添加 1.document.write() <html xmlns="http://www.w3.org/1999/xhtml"> <head&g ...
- Java动态编程---动态代理
java中动态编程用到的技术有:反射(动态代理),javassist和ASM,这几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象.在实现成 ...

