1. 概述

Robolectric(http://robolectric.org/)是一款支持在桌面JVM模拟Android环境的测试框架,通过shadow包下的类来截取view、activity等类的调用,代替它们运行。举个例子说明一下,比如android里面有个类叫TextView,他们实现了一个类叫ShadowTextView。这个类基本上实现了TextView的所有公共接口,假设你在unit test里面写到String text = textView.getText().toString();。在这个unit test运行的时候,Robolectric会自动判断你调用了Android相关的代码textView.getText(),然后这个调用过程在底层截取了,转到ShadowTextView的getText实现。而ShadowTextView是真正实现了getText这个方法的,所以这个过程便可以正常执行。

除此之外,Robolectric还为shadow类额外提供了很多接口,可以读取对应的Android类的一些状态。

对于一些测试对象依赖度较高而需要解除依赖的场景,可以借助mock框架。

对于网络请求还可以进行网络请求测试。

2. 配置

在module 的build.gradle中添加测试依赖:

dependencies {
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.3.2"
testCompile 'org.robolectric:shadows-httpclient:3.3.2'
}

3. 基本用法汇总

  1. 创建测试类

  2. 在类的前面添加:

    @RunWith(RobolectricTestRunner.class):指定Junit的构建程序为RobolectricTestRunner

    @Config(constants=BuildConfig.class,sdk=21):为每个类或测试的基础上添加配置设置。

    可以设置sdk版本,buildConfig、manifest等。

    @RunWith(RobolectricTestRunner.class)
    @Config(constants = BuildConfig.class, sdk=21)
    public class BrowseTest { @Before
    public void before(){
    ...
    } @Test
    public void test()throws Exception{
    ...
    }
    }
  3. 具体事例地址:

    https://github.com/robolectric/robolectric-samples

    google官方的一个例子

  4. 网络访问

  • 利用Robolectric进行测试,可以使用框架自带的FakeHttp,该功能可以模拟网络访问,也可以真实访问网络。

  • 访问真实网络

@Test
public void requestTest() throws Exception {
//设置是否拦截真实的请求。若不设置则默认为TRUE,若设false,则会真实访问网络。
FakeHttp.getFakeHttpLayer().interceptHttpRequests(false);
String url="https://api.douban.com/v2/movie/celebrity/1054395";
URL url1=new URL(url);
HttpURLConnection connection= (HttpURLConnection) url1.openConnection();
InputStream inputStream = connection.getInputStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream));
String result=new String(reader.readLine().getBytes(),"UTF-8");
System.out.println(result);
}
  • 模拟网络访问
@Test
public void fakeRequestTest() throws IOException {
//设置拦截真实请求
FakeHttp.getFakeHttpLayer().interceptHttpRequests(true);
//模拟返回
ProtocolVersion version=new ProtocolVersion("HTTP",1,1);
HttpResponse httpResponse=new BasicHttpResponse(version,400,"OK");
//设置默认返回,所有请求返回这个
FakeHttp.setDefaultHttpResponse(httpResponse);
//添加一个返回规则,指定一个请求的期望返回
FakeHttp.addHttpResponseRule("http://www.baidu.com",httpResponse);
//执行请求
HttpGet get=new HttpGet("http://www.baidu.com");
HttpResponse response=new DefaultHttpClient().execute(get); //若response==httpResponse则通过。
Assert.assertThat(response,is(httpResponse));
}
  1. 注意事项(遇到的坑)

    • volley框架不返回结果

      由于volley一般是将结果回调到UI线程进行处理的,但是Robolectric对Ui线程的模拟支持似乎不太好,所以会导致能运行但是没结果的现象。
    final CountDownLatch latch = new CountDownLatch(1);
    
    //设置是否拦截真实的请求。若不设置则默认为TRUE,若设false,则会真实访问网络。
    FakeHttp.getFakeHttpLayer().interceptHttpRequests(false);
    String url="https://api.douban.com/v2/movie/celebrity/1054395";
    StringRequest req=new StringRequest(Request.Method.GET, url,
    new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
    System.out.println(response);
    latch.countDown();
    }
    },
    new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) { }
    }
    );
    HttpStack stack=new HurlStack();
    Network network=new BasicNetwork(stack);
    ResponseDelivery responseDelivery=new ExecutorDelivery(Executors.newSingleThreadExecutor());
    RequestQueue queue=new RequestQueue(new NoCache(),network,4,responseDelivery);
    queue.start();
    queue.add(req);
    • 运行测试用例返回以下错误:
        java.lang.VerifyError: Expecting a stackmap frame at branch target 45
    Exception Details:
    Location:
    com/umeng/message/NotificationProxyBroadcastReceiver.a(Landroid/content/Context;)V @13: ifnonnull
    Reason:
    Expected stackmap frame at this location.
    Bytecode:
    0x0000000: 2bb6 0027 2bb6 0028 b600 2e4d 2cc7 0020
    0x0000010: b200 21bb 001e 59b7 003d 120c b600 3e2b
    0x0000020: b600 28b6 003e b600 3fb8 002f b12c 01b6
    0x0000030: 002d 572c 1205 b600 2a57 2b2c b600 29b2
    0x0000040: 0021 bb00 1e59 b700 3d12 0db6 003e 2bb6
    0x0000050: 0028 b600 3eb6 003f b800 30b1 at java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
    at java.lang.Class.getConstructor0(Class.java:3075)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at org.robolectric.util.ReflectionHelpers.callConstructor(ReflectionHelpers.java:319)
    at org.robolectric.internal.bytecode.ShadowImpl.newInstanceOf(ShadowImpl.java:20)
    at org.robolectric.shadow.api.Shadow.newInstanceOf(Shadow.java:35)
    at org.robolectric.shadows.ShadowApplication.registerBroadcastReceivers(ShadowApplication.java:138)
    at org.robolectric.shadows.ShadowApplication.bind(ShadowApplication.java:127)
    at org.robolectric.shadows.CoreShadowsAdapter.bind(CoreShadowsAdapter.java:71)
    at org.robolectric.android.internal.ParallelUniverse.setUpApplicationState(ParallelUniverse.java:107)
    at org.robolectric.RobolectricTestRunner.beforeTest(RobolectricTestRunner.java:290)
    at org.robolectric.internal.SandboxTestRunner$2.evaluate(SandboxTestRunner.java:203)
    at org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:109)
    at org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:36)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.robolectric.internal.SandboxTestRunner$1.evaluate(SandboxTestRunner.java:63)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

    解决方案:

    https://github.com/robolectric/robolectric-gradle-plugin/issues/144#issuecomment-189561165

    打开菜单Run->Edit Configuration

    按照以下设置: 在VM option中添加-ea -noverify

    这样就可以了

参考的文章:

http://www.vogella.com/tutorials/Robolectric/article.html#shadow-objects

http://blog.csdn.net/weijianfeng1990912/article/details/51020423

Robolectric测试框架使用笔记的更多相关文章

  1. Robot Framework测试框架学习笔记

    一.Robot Framework框架简介         Robot Framework是一种基于Python的可扩展关键字驱动自动化测试框架,通常用于端到端的可接收测试和可接收测试驱动的开发.可以 ...

  2. TestNg测试框架使用笔记

    Gradle支持TestNG test { useTestNG(){ //指定testng配置文件 suites(file('src/test/resources/testng.xml')) } } ...

  3. 测试框架Mockito使用笔记

    Mockito,测试框架,语法简单,功能强大! 静态.私有.构造等方法测试需要配合PowerMock,PowerMock有Mockito和EasyMock两个版本,语法相同,本文只介绍Mockito. ...

  4. Android开源测试框架学习

    近期因工作需要,分析了一些Android的测试框架,在这也分享下整理完的资料. Android测试大致分三大块: 代码层测试 用户操作模拟,功能测试 安装部署及稳定性测试 代码层测试 对于一般java ...

  5. 消灭Bug!十款免费移动应用测试框架推荐

      对于移动应用开发者而言,Bug往往是最让人头疼的一大问题.不同于时时刻刻可以修补的Web App,移动App中的Bug往往隐藏得很深,甚至有时候等到用户使用才显现出来,这么一来开发者搞不好就会赔了 ...

  6. phalcon(费尔康)框架学习笔记

    phalcon(费尔康)框架学习笔记 http://www.qixing318.com/article/phalcon-framework-to-study-notes.html 目录结构   pha ...

  7. 十大免费移动程序测试框架(Android/iOS)

    十大免费移动程序测试框架(Android/iOS) 概述:本文将介绍10款免费移动程序测试框架,帮助开发人员简化测试流程,一起来看看吧. Bug是移动开发者最头痛的一大问题.不同于Web应用程序开发, ...

  8. JavaSE中线程与并行API框架学习笔记1——线程是什么?

    前言:虽然工作了三年,但是几乎没有使用到多线程之类的内容.这其实是工作与学习的矛盾.我们在公司上班,很多时候都只是在处理业务代码,很少接触底层技术. 可是你不可能一辈子都写业务代码,而且跳槽之后新单位 ...

  9. JavaSE中线程与并行API框架学习笔记——线程为什么会不安全?

    前言:休整一个多月之后,终于开始投简历了.这段时间休息了一阵子,又病了几天,真正用来复习准备的时间其实并不多.说实话,心里不是非常有底气. 这可能是学生时代遗留的思维惯性--总想着做好万全准备才去做事 ...

随机推荐

  1. UVa 10285 最长的滑雪路径(DAG上的最长路)

    https://vjudge.net/problem/UVA-10285 题意: 在一个R*C的整数矩阵上找一条高度严格递减的最长路.起点任意,但每次只能沿着上下左右4个方向之一走一格,并且不能走出矩 ...

  2. UOJ【UR #12】实验室外的攻防战

    题意: 给出一个排列$A$,问是否能够经过以下若干次变换变为排列$B$ 变换:若${A_i> A_i+1}$,可以${swap(A_i,A_i+1)}$ 考虑一个数字从A排列到B排列连出来的路径 ...

  3. codeforces 352 div 2 C.Recycling Bottles 贪心

    C. Recycling Bottles time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  4. Win7下怎么设置让远程桌面连接记住密码下次登录不需再输入

    远程桌面连接功能想必大家都不会陌生吧,特别是使用VPS服务器的用户们经常会用到,为了服务器的安全每次都会把密码设置的很复制,但是这样也有一个麻烦,就是每次要桌面远程连接的时候都要输入这么复杂的密码,很 ...

  5. Spring Boot中注解事务

  6. Short Encoding of Words

    2018-07-02 09:48:48 问题描述: 问题求解: 方法一.问题给了规模n = 2000,也就是说用BF在O(n^2)的时间复杂度可以过,因此,第一个方法就是BF,但是需要注意的是这里已经 ...

  7. 一个简单可参考的API网关架构设计

    网关一词较早出现在网络设备里面,比如两个相互独立的局域网段之间通过路由器或者桥接设备进行通信, 这中间的路由或者桥接设备我们称之为网关. 相应的 API 网关将各系统对外暴露的服务聚合起来,所有要调用 ...

  8. Java 写数据到文件

    private boolean writeToFile(BusGpsBean gpsBean) { String dataStr = DateUtil.date2String(new Date(), ...

  9. 一个表单里,如果有<button>标签存在,它会自动提交表单

    可以用button代替input type=”submit”吗? 在ie下,<button>标记恐怕还存在几个不大不小的问题. 在一个表单里,如果有一个以上"submit&quo ...

  10. 使用 Python 的 Socket 模块构建一个 UDP 扫描工具

    译文:oschina 英文:bt3gl 当涉及到对一些目标网络的侦察时,出发点无疑是首先发现宿主主机.这个任务还可能包含嗅探和解析网络中数据包的能力. 几周前,我曾经谈到了如何使用Wireshark来 ...