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上运行你的测试代码,能在几秒钟之内快速验证,通过体验之后,它确实非常高效,编写测试代码反而加速了开发效率。

具体的原理描述可参见:

Robolectic官网

Robolectic介绍

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 探索之路的更多相关文章

  1. Android 单元测试(junit、mockito、robolectric)

    1.运用JUnit4 进行单元测试 首先在工程的 src 文件夹内创建 test 和 test/java 文件夹. 打开工程的 build.gradle(Module:app)文件,添加JUnit4依 ...

  2. Android studio下gradle Robolectric单元测试配置

    android studio下gradle Robolectric单元测试配置 1.Robolectric Robolectric是一个基于junit之上的单元测试框架.它并不依赖于Android提供 ...

  3. Robolectric 配置

    费了些工夫,已配好,按记录留记录 按官网操作http://robolectric.org/getting-started/ 1引包 testCompile "org.robolectric: ...

  4. 在Android Studio中用Gradle添加Robolectric

    我们用Robolectric测试的话需要在gradle中进行配置,国内的详细教程太过简易,而且很多是低版本下的配置方案.所以经过本人的仔细摸索,找到了现在高版本中的配置方案,主要还是参考了官网的配置教 ...

  5. robolectric环境的搭建

    最近在学习测试驱动开发(Test-Driven Development),测试驱动开始是极限编程的一种方式,提倡在真正编写代码之前先根据需求编写测试代码(当然这个测试代码是不可能通过的),然后根据测试 ...

  6. Extending Robolectric

    Robolectric is a work in progress, and we welcome contributions from the community. We encourage dev ...

  7. Configuring Robolectric

    There are numerous ways to customize how Robolectric behaves at runtime. Config Annotation The prima ...

  8. Robolectric Test-Drive Your Android Code

    Running tests on an Android emulator or device is slow! Building, deploying, and launching the app o ...

  9. OpenCV探索之路(二十四)图像拼接和图像融合技术

    图像拼接在实际的应用场景很广,比如无人机航拍,遥感图像等等,图像拼接是进一步做图像理解基础步骤,拼接效果的好坏直接影响接下来的工作,所以一个好的图像拼接算法非常重要. 再举一个身边的例子吧,你用你的手 ...

随机推荐

  1. Oracle EBS-SQL (GL-5):从发票追溯到接收

    SELECT destination_type_code, distribution_line_number, line_type, amount,vat_code, tax_code_id, tax ...

  2. 3.21 采购订单导入MDS

    3.21.1   业务方案描述 同一企业集团内部的不同法人之间,双方间内部往来业务频繁.受集团财务各自独立核算的要求,买方和卖方间采用买卖方式进行业务运作和财务结算. 对于买方,按照内部商定的协议价格 ...

  3. perl 分析binlog 定位错误sql 思路

    1. 获取需要的binlog 日志: [root@zjzc01 binlog]# mysqlbinlog --start-datetime='2016-08-01 00:00:00' --stop-d ...

  4. php使用check box

    if (isset($_POST['submit'])) { foreach ($_POST['todelete'] as $delete_id) { //这里是循环遍历这个数组 todelete 每 ...

  5. Saiku对Measure(指标)查询结果进行计算后显示的方法

    在通过Saiku查询数据的时候,当需要改变查询结果的显示方式的时候,可以添加formatString属性,但是当需要计算查询结果的时候,则需要使用CellFormatter,使用方法如下: <M ...

  6. IOS系列——NStimer

    Timer经常使用的一些东西 1. 初始化 timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@select ...

  7. [LeetCode] Search a 2D Matrix [25]

    题目 Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the fo ...

  8. 初识Maven

    今天开始学习怎样使用maven,听起来挺神奇的东西,我们来一步一步的加以剖析. Maven的一些具体的论文的东西,网上很多博客介绍,这里我就不逐一介绍,下面我们从安装maven开始讲解: (1)Mav ...

  9. HTML5的绘图的支持

    一.简单介绍canvas元素 <canvas.../>是HTML5新增的一个元素,该元素用于绘制图形.实际上<canvas../>只是相当于一张画布. 它除了可以指定通用属性外 ...

  10. redolog

    总结: 教育我们:redo文件组需要创建多个组,建议至少3个,每个组至少2个文件,最好放在不同的硬盘上. 当联机日志文件丢失一般使用清除日志的方法: alter database clear logf ...