快速的查看返回数据与数据的处理。(一般我们都是将写好的代码跑到手机上,点击到对应页面的对应按钮)
异常信息的返回与处理。比如一个接口返回一个列表数据,如果列表为空呢?(找后台给我们模拟数据)网速不好呢?(不知道怎么搞…)网络异常呢?(关闭网络)
后台有部分接口没有写好,你就只能等他了。
不知道上面的这三点有没有戳到你的痛处。如果扎心了,那么老铁你就有必要掌握今天的内容。
1.请求接口
我们就使用网络请求三件套(retrofit + okhttp + rxjava2)来举例。
首先添加一下依赖,同时记得加网络权限。
//RxJava compile 'io.reactivex.rxjava2:rxjava:2.1.7' //RxAndroid compile 'io.reactivex.rxjava2:rxandroid:2.0.1' //okhttp compile "com.squareup.okhttp3:okhttp:3.9.1" //Retrofit compile ("com.squareup.retrofit2:retrofit:2.3.0"){ exclude module: 'okhttp' } compile ("com.squareup.retrofit2:adapter-rxjava2:2.3.0"){ exclude module: 'rxjava' } compile "com.squareup.retrofit2:converter-gson:2.3.0" |
测试接口:
public interface GithubApi { String BASE_URL = "https://api.github.com/"; @GET("users/{username}") Observable<User> getUser(@Path("username") String username); } |
Retrofit的初始化,我们使用LoggingInterceptor来打印返回数据。
public class GithubService { private static Retrofit retrofit = new Retrofit.Builder() .baseUrl(GithubApi.BASE_URL) .client(getOkHttpClient()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); public static GithubApi createGithubService() { return retrofit.create(GithubApi.class); } private static OkHttpClient getOkHttpClient(){ return new OkHttpClient.Builder() .addInterceptor(new LoggingInterceptor()) .build(); } } |
测试代码:
@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 23) public class ResponseTest { @Before public void setUp() { ShadowLog.stream = System.out; initRxJava2(); } private void initRxJava2() { RxJavaPlugins.reset(); RxJavaPlugins.setIoSchedulerHandler(new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { return Schedulers.trampoline(); } }); RxAndroidPlugins.reset(); RxAndroidPlugins.setMainThreadSchedulerHandler(new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { return Schedulers.trampoline(); } }); } @Test public void getUserTest() { GithubService.createGithubService() .getUser("simplezhli") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<User>() { @Override public void onSubscribe(Disposable d) {} @Override public void onNext(User user) { assertEquals("唯鹿", user.name); assertEquals("http://blog.csdn.net/qq_17766199", user.blog); } @Override public void onError(Throwable e) { Log.e("Test", e.toString()); } @Override public void onComplete() {} }); } } |
上面的代码中,因为网络请求是异步的,所以我们直接测试是不能直接拿到数据,因此无法打印出Log以及测试。所以我们使用initRxJava2()方法将异步转化为同步。这样我们就可以看到返回信息。测试结果如下:
上面的例子为了简单直观的说明,所以将请求接口的方法写到了测试类中,实际中我们可以Mock方法所在的类直接调用请求方法。配合Robolectric对View控件的状态进行测试。
如果你觉得每次测试都要加initRxJava2这段方法很麻烦,你可以抽象出来,或者使用@Rule。
public class RxJavaRule implements TestRule { @Override public Statement apply(final Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { RxJavaPlugins.reset(); RxJavaPlugins.setIoSchedulerHandler(new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { return Schedulers.trampoline(); } }); RxAndroidPlugins.reset(); RxAndroidPlugins.setMainThreadSchedulerHandler(new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { return Schedulers.trampoline(); } }); base.evaluate(); } }; } } |
2.模拟数据
1.使用拦截器模拟数据
利用okhttp的拦截器模拟响应数据。
public class MockInterceptor implements Interceptor { private final String responseString; //你要模拟返回的数据 public MockInterceptor(String responseString) { this.responseString = responseString; } @Override public Response intercept(Interceptor.Chain chain) throws IOException { Response response = new Response.Builder() .code(200) .message(responseString) .request(chain.request()) .protocol(Protocol.HTTP_1_0) .body(ResponseBody.create(MediaType.parse("application/json"), responseString.getBytes())) .addHeader("content-type", "application/json") .build(); return response; } } |
测试代码:
@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 23) public class MockGithubServiceTest { private GithubApi mockGithubService; @Rule public RxJavaRule rule = new RxJavaRule(); @Before public void setUp() throws URISyntaxException { ShadowLog.stream = System.out; //定义Http Client,并添加拦截器 OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new LoggingInterceptor()) .addInterceptor(new MockInterceptor("json数据"))//<-- 添加拦截器 .build(); //设置Http Client Retrofit retrofit = new Retrofit.Builder() .baseUrl(GithubApi.BASE_URL) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); mockGithubService = retrofit.create(GithubApi.class); } @Test public void getUserTest() throws Exception { mockGithubService.getUser("weilu") //<-- 这里传入错误的用户名 .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<User>() { @Override public void onSubscribe(Disposable d) {} @Override public void onNext(User user) { assertEquals("唯鹿", user.name); assertEquals("http://blog.csdn.net/qq_17766199", user.blog); } @Override public void onError(Throwable e) { Log.e("Test", e.toString()); } @Override public void onComplete() {} }); } } |
虽然我们传入了错误的用户名,但是我们模拟的响应信息已经提前设定好了,所以测试结果不变。
利用这个思路,我们可以修改MockInterceptor的code,模拟404的情况。
2.MockWebServer
MockWebServer是square出品的跟随okhttp一起发布,用来Mock服务器行为的库。MockWebServer能帮我们做的事情:
可以设置http response的header、body、status code等。
可以记录接收到的请求,获取请求的body、header、method、path、HTTP version。
可以模拟网速慢的网络环境。
提供Dispatcher,让mockWebServer可以根据不同的请求进行不同的反馈。
添加依赖:
testCompile 'com.squareup.okhttp3:mockwebserver:3.9.1'
测试代码:
@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 23) public class MockWebServerTest { private GithubApi mockGithubService; private MockWebServer server; @Rule public RxJavaRule rule = new RxJavaRule(); @Before public void setUp(){ ShadowLog.stream = System.out; // 创建一个 MockWebServer server = new MockWebServer(); //设置响应,默认返回http code是 200 MockResponse mockResponse = new MockResponse() .addHeader("Content-Type", "application/json;charset=utf-8") .addHeader("Cache-Control", "no-cache") .setBody("{\"id\": 12456431, " + " \"name\": \"唯鹿\"," + " \"blog\": \"http://blog.csdn.net/qq_17766199\"}"); MockResponse mockResponse1 = new MockResponse() .addHeader("Content-Type", "application/json;charset=utf-8") .setResponseCode(404) .throttleBody(5, 1, TimeUnit.SECONDS) //一秒传递5个字节,模拟网速慢的情况 .setBody("{\"error\": \"网络异常\"}"); server.enqueue(mockResponse); //成功响应 server.enqueue(mockResponse1);//失败响应 OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new LoggingInterceptor()) .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://" + server.getHostName() + ":" + server.getPort() + "/") //设置对应的Host与端口号 .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); mockGithubService = retrofit.create(GithubApi.class); } @Test public void getUserTest() throws Exception { //请求不变 mockGithubService.getUser("simplezhli") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<User>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(User user) { assertEquals("唯鹿", user.name); assertEquals("http://blog.csdn.net/qq_17766199", user.blog); } @Override public void onError(Throwable e) { Log.e("Test", e.toString()); } @Override public void onComplete() { } }); //验证我们的请求客户端是否按预期生成了请求 RecordedRequest request = server.takeRequest(); assertEquals("GET /users/simplezhli HTTP/1.1", request.getRequestLine()); assertEquals("okhttp/3.9.1", request.getHeader("User-Agent")); // 关闭服务 server.shutdown(); } } |
代码中的注释写的很清楚了,就不用过多的解释了,使用起来非常的简单。
执行结果(成功):
失败结果:
默认情况下 MockWebServer 预置的响应是先进先出的。这样可能对你的测试有限制,这时可以通过Dispatcher来处理,比如通过请求的路径来选择转发。
Dispatcher dispatcher = new Dispatcher() { @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { if (request.getPath().equals("/users/simplezhli")){ return new MockResponse() .addHeader("Content-Type", "application/json;charset=utf-8") .addHeader("Cache-Control", "no-cache") .setBody("{\"id\": 12456431, " + " \"name\": \"唯鹿\"," + " \"blog\": \"http://blog.csdn.net/qq_17766199\"}"); } else { return new MockResponse() .addHeader("Content-Type", "application/json;charset=utf-8") .setResponseCode(404) .throttleBody(5, 1, TimeUnit.SECONDS) //一秒传递5个字节 .setBody("{\"error\": \"网络异常\"}"); } } }; server.setDispatcher(dispatcher); //设置Dispatcher |
通过上面的例子,是不是可以很好的解决你的痛点,希望对你有帮助。只要我们有和后台有开发文档,约定好数据格式与字段名,那么我们可以更敏捷的去做我们的开发。本篇所有代码已上传至Github。希望大家多多点赞支持!
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。