使用Robolectric模拟Http请求和响应

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载自夜明的孤行灯

本文链接地址: https://www.huangyunkun.com/2014/04/08/mock-http-in-android-with-robolectric/

习惯了用Gradle管理依赖以后突然回到Android开发,才发现Idea 12 对Android-Gradle项目支持很烂,只有手动下载依赖。

Android App的开发比普通Java Web或者Java Application开发麻烦很多。主要是集成测试使用的Instrumented Test必须在模拟器或者真机上测试,挺耽误时间的。
这样的测试通常粒度较大,测试的编写和维护较为困难,而最为重要的是,由于速度慢,如果使用TDD来进行开发,根本无法达到快速开发的要求。

所以在Android开发中,尽可能多使用单元测试保证每个模块的正确性,来尽可能保证集成测试的成功率。

 

基于Mockito的简单测试

Android SDK也有测试框架,但是那个不是基于普通JVM的,运行起来还是需要模拟器或者真机。要基于普通JVM测试,就必须让模块尽可能的不依赖于Android的东西,需要依赖的也用Mockito等Mock掉。

如果需要Mock的是自己的代码还好,如果是Android的东西,还需要处理各种状态和返回量。

比如我们的APP需要调用WifiManager,如果只是检测Wifi是否开启还比较方便,只需要mock以后设定when就行了。

WifiManager wifiManager = mock(WifiManager.class);
when(wifiManager.isWifiEnabled()).thenReturn(true);

如果我们需要扫描Wifi,然后检测Wifi状态等,工作就变动了。你需要仔细思考Android设备正常情况下的状态变动,还需要构造哦一个List<ScanResult>

WifiManager wifiManager = mock(WifiManager.class);
when(wifiManager.getScanResults()).thenReturn(Lists.<ScanResult>newArrayList());
when(wifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED).thenReturn(WifiManager.WIFI_STATE_DISABLED).thenReturn(WifiManager.WIFI_STATE_ENABLED);

其实以上问题还好,因为我们mock出了WifiManager以后将它传入调用的类就行了。

publicclass LoginService {
private WifiManager wifimanager;
publicLoginService(WifiManager wifimanager)
{
this.wifimanager=wifimanager;
}
}

但是我现在做的应用需要访问网络内容,使用的DefaultHttpClient,它是写在业务代码里面的,虽然也可以解耦合做和wifimanager同样的处理,但是着既不合理也不方便。

Robolectric

虽然可以使用mockito这类框架来mock掉一部分依赖,但是由于Android平台自身的复杂性,mock的正确性很难保证。

而Robolectric是一个单元测试框架,它提供了一个测试用的Android模拟,目前只提供了android-base-4.1.2_r1_rc,但是足够使用了。一方面它运行于普通JVM,保证了速度,另一方面,它模拟了绝大部分繁琐的功能,比如布局解析,资源处理。

这样就可以像对待黑箱测试一样的编写测试,集中精力在业务处理上,而不是Android的实现上。

Robolectric模拟Http请求和响应

Robolectric提供了一个虚假的Http层,你可以拦截正常的请求,用另外一个替换掉;也可以不拦截真实请求,使用真的Http层。

当然单元测试中尽可能不依赖外界,所以一般都拦截并替换掉。

如果逻辑简单,可以直接使用setDefaultHttpResponse设定Http层返回的结果,这样所有的请求都是同样的响应。

Robolectric.setDefaultHttpResponse(200,"response body");
Robolectric.setDefaultHttpResponse(400,null);

如果你需要按照顺序进行多个响应,可以使用addPendingHTTPResponse,以此添加你需要的响应。

Robolectric.addPendingHttpResponse(400,null);
Robolectric.addPendingHttpResponse(200,"some content");

通过addPendingHTTPResponse方法添加的响应会进入队列中,如果需要清空可以使用clearPendingHTTPResponses

通过规则模拟HTTP请求和响应

上面的方法都比较简单,如果处理的情况比较复杂,需要判断HTTP头和参数,或者希望HTTP层可以灵活处理各种情况,可以使用addHTTPResponseRule

最常见的判断是网址

ProtocolVersion httpProtocolVersion =new ProtocolVersion("HTTP",1,1);
HttpResponse successResponse =
new BasicHttpResponse(httpProtocolVersion,200,"OK");
successResponse.setEntity(
new StringEntity("response body"));
Robolectric.addHttpResponseRule("http://www.myserver.com/successfullcall", successResponse);

HttpResponse failedResponse =
new BasicHttpResponse(httpProtocolVersion,400,"Bad Request");
Robolectric.addHttpResponseRule("http://www.myserver.com/failedcall", failedResponse);

这样如果访问sucessfullcall就会返回一个200响应,内容为”response body”,如果访问failedcall会返回400响应。

动态模拟HTTP请求和响应

如果还有复杂的参数,或者需要区分method等等,还可以实现ResponseRule接口,在matches中判断是否响应,在getResponse中判断是否响应。最后通过addHttpResponseRule添加。

class NumberResponseRule implements ResponseRule {
int count=0;
@Override
public HttpResponsegetResponse()throws HttpException, IOException {
HttpResponse response=new BasicHttpResponse(
new ProtocolVersion("HTTP",1,1),200,"OK");
response.setEntity(new StringEntity('返回值:'+Integer.toString(count));
count++;

return response;
}

@Override
publicbooleanmatches(HttpRequest request) {
String uri = request.getRequestLine().getUri();
if (uri.equals("http://www.myserver.com/getCount")) {
returntrue;
}
returnfalse;
}
}
Robolectric.getFakeHttpLayer().addHttpResponseRule(new NumberResponse());

参考

Robolectric

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载自夜明的孤行灯

本文链接地址: https://www.huangyunkun.com/2014/04/08/mock-http-in-android-with-robolectric/

发表评论