Android单元测试利器–Robolectric VolleyDemo

2016/07/21

本文原创作者:Cloud Chou. 欢迎转载,请注明出处和本文链接

本节讲述如何使用Robolectric+PowerMock测试需要在UI线程执行的逻辑,比如Volley框架,在后台线程中请求网络,请求完成后在UI线程里通过Listener接口通知请求完成,并传递请求回来的数据。

Android单元测试系列文章的代码都可以在Github上找到: https://github.com/cloudchou/RobolectricDemo

使用Robolectric+Powermock测试UI线程逻辑

在使用Robolectric框架测试需要在UI线程执行的逻辑时,需要注意的是在Android平台UI线程会轮询消息队列,然后从消息队列里取出消息,并将消息分发给Handler处理,UI线程执行的是轮询消息队列的死循环。但是在Robolectric框架中运行时,UI线程默认情况下并不会轮询消息队列,而需要在测试用例代码里主动驱动UI线程从消息队列里取出消息进行分发。测试用例执行时并不在UI线程,而是在单独的线程中,所以它可以主动驱动UI线程分发消息。

本节使用Volley请求来讲述如何针对这种情况进行测试,首先我们来看被测试的类VolleyRequest,它非常简单,使用了OkHttp作为传输层,请求http://www.mocky.io/v2/5597d86a6344715505576725,然后将请求的数据保存下来。源代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class VolleyRequester {
    private static final String TAG = VolleyRequester.class.getSimpleName();
    private RequestQueue mRequestQueue;
    private Context mContext;
    private volatile String mResponseStr;

    public void request(Context context) {
        mContext = context;
        RequestQueue volleyRequestQueue = getVolleyRequestQueue(mContext);
        //请求的url 请求该URL会返回一段json字符串
        //测试时 如果将其改成一个 无法访问的网址 就可以测试访问失败的情况
        String url = "http://www.mocky.io/v2/5597d86a6344715505576725";
        Response.Listener<String> dataListener = new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                System.out.println("received response. ");
                mResponseStr = response;
            }
        };
        Response.ErrorListener errorListener = new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                //请求失败 会打印error日志
                SLog.e(TAG, "request failed");
            }
        };
        StringRequest request = new StringRequest(Request.Method.POST, url, dataListener, errorListener);
        volleyRequestQueue.add(request);
    }

    public String getResponseString() {
        return mResponseStr;
    }

    private RequestQueue getVolleyRequestQueue(Context ctx) {
        if (mRequestQueue == null) {
            // 使用OkHttp 作为传输层
            mRequestQueue = Volley.newRequestQueue(ctx, new OkHttpStack(new OkHttpClient()));
        }
        return mRequestQueue;
    }

    private static class OkHttpStack extends HurlStack {
        private OkHttpClient mOkHttpClient;

        public OkHttpStack(OkHttpClient okHttpClient) {
            mOkHttpClient = okHttpClient;
        }

        @Override
        protected HttpURLConnection createConnection(URL url) throws IOException {
            OkUrlFactory factory = new OkUrlFactory(mOkHttpClient);
            return factory.open(url);
        }
    }
}

接下来我们看看如何驱动主线程轮询消息队列,测试代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
 * date 2016/7/3
 *
 * @author Cloud
 * @version 1.1
 * @since Ver 1.1
 */
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
//必须写如下代码 让PowerMock 忽略Robolectric的所有注入 这里因为要使用https 所以还需要忽略ssl
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "javax.net.ssl.*"})
//因为我们是针对类做静态函数的mock,所以必须使用PrepareForTest说明我们要mock的类
@PrepareForTest({SLog.class})
public class VolleyRequesterTest {

    //不可缺少的代码 表明使用Powermock执行单元测试,虽然我们使用的是RoblectricGradleTestRunner来执行单元测试
    //但是添加了如下代码后RoblectricGradleTestRunner会调用PowerMock的TestRunner去执行单元测试
    @Rule
    public PowerMockRule rule = new PowerMockRule();

    @Before
    public void setup() {
        PowerMockito.mockStatic(SLog.class);
    }

    @Test
    public void testRequest() throws Exception {
        PowerMockito.spy(SLog.class);
        VolleyRequester requester = new VolleyRequester();
        //调用请求方法后 volley 会开启后台线程去做真正的请求, 请求完毕后会在主线程上
        //调用Listener.onResponse方法通知请求完毕
        //但是主线程是一个有Handler的线程,Robolectric框架让主线程不轮询消息队列
        //必须在测试方法里主动驱动主线程轮询消息队列,针对消息进行处理
        //否则永远不会在UI线程上通知请求完毕
        requester.request(RuntimeEnvironment.application);
        //获取主线程的消息队列的调度者,通过它可以知道消息队列的情况
        //并驱动主线程主动轮询消息队列
        Scheduler scheduler = Robolectric.getForegroundThreadScheduler();
        //因为调用请求方法后 后台线程请求需要一段时间才能请求完毕,然后才会通知主线程
        // 所以在这里进行等待,直到消息队列里存在消息
        while (scheduler.size() == 0) {
            Thread.sleep(500);
        }
        //轮询消息队列,这样就会在主线程进行通知
        scheduler.runOneTask();
        // 校验 请求是否失败
        PowerMockito.verifyStatic(times(0));
        SLog.e(VolleyRequester.class.getSimpleName(), "request failed");
        //如果没有失败 则打印请求回来的字符串
        String responseString = requester.getResponseString();
        System.out.println("response str:\\n" + responseString);
    }

}

从上述代码可以看到我们可以通过获取Scheduler对象来判断消息队列中是否有消息,并调用Scheduler的runOneTask方法进行消息分发,这样就驱动了主线程进行消息轮询,执行结果如下所示:

volley_demo

¥打赏5毛

取消

感谢您的支持,我会继续努力的!

扫码支持
赏个5毛,支持我把

打开支付宝扫一扫,即可进行扫码打赏哦

本篇目录