Robolectric测试框架使用笔记

摘要:
当这个单元测试运行时,Roboctric将自动确定您已经调用了Android相关代码textView getText(),然后这个调用过程在底层被拦截,并转到ShadowTextView的getText实现。此外,Roboelectric还为shadow类提供了许多额外的接口,可以读取相应Android类的一些状态。明确肯定assertThat;}注意:截击框架不会返回结果。因为凌空通常会将结果回调到UI线程进行处理,但Robolectric对UI线程的模拟支持似乎不是很好,这会导致它可以运行但没有结果的现象。

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
    img

    这样就可以了

参考的文章:
http://www.vogella.com/tutorials/Robolectric/article.html#shadow-objects
http://blog.csdn.net/weijianfeng1990912/article/details/51020423

免责声明:文章转载自《Robolectric测试框架使用笔记》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ansible 调试 debug 一介凡人Azure DevOps Server:使用Postman批量删除测试用例下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

spring装配bean的三种方式及其混合装配

在spring容器中装配bean有三种基本方式和混合装配方式: 隐式的bean自动发现机制和自动装配 在java中进行显式配置 在xml中配置 混合装配(在多个java文件中配置、在JavaConfig中引用XML配置、在XML中引用JavaConfig配置) 一、使用自动化方式装配bean示例: 1:创建maven项目并引入依赖: <?xml...

多个类用@feignclient标注同一个服务,出错问题:Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.Caused by: org.springframework.beans.factory.support.Bea..

如果标注了两个或以上类 @FeignClient 标注同一个 服务名称 调用方会主配置类启动会报错   测试类报错   java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.D...

JUnit —— TestSuite 的使用

首先说一下,suite ,中文是 一套,一组的意思。 那么,TestSuite,顾名思义,就是用来运行一组测试的。 如何使用呢 ? 假设有个这样的测试类StudentDAOTest ,代码如下: 1 packageorg.young.junit.testsuite; 2 3 importjunit.framework.TestCase; 4 5 imp...

IDEA 单元测试 导入JUnit4到项目

一、IDEA自带JUnit4的jar包,现在让我们来导入。 Step 1. IDEA最上面一栏的菜单栏中,选File->Project Structure(从上往下第11个),弹出窗口左边有一个列表,选Module。 Step 2. 右侧有一个带3个标签的窗口,选Dependencies标签 Step 3. 下面的列表框列出了项目的jar包,右侧有...

JUnit使用简介

JUnit使用简介       Unit是一个Java语言的单元测试框架。它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个。 JUnit有它自己的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit作为单元测试的工具。       JUnit是由 Erich Ga...

Eclipse开发Maven项目提示:程序包org.junit不存在解决方案

1、产生的问题: maven项目执行单元测试可以正常执行,但是执行maven命令test,则报一下异常: org.apache.maven.plugins:maven-compiler-plugin:2.5.1:testCompile (default-testCompile) on project support: Compilation failure...