Robolectric測試框架使用筆記

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框架。
對於網絡請求還能夠進行網絡請求測試。html

2. 配置

在module 的build.gradle中添加測試依賴:java

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等。android

    @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官方的一個例子git

  4. 網絡訪問
  • 利用Robolectric進行測試,可使用框架自帶的FakeHttp,該功能能夠模擬網絡訪問,也能夠真實訪問網絡。github

  • 訪問真實網絡api

@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網絡

    這樣就能夠了app

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

相關文章
相關標籤/搜索