Android单元测试(五):网络接口测试

 温馨提示:如果你不太熟悉单元测试,可以先看下之前四篇基础框架使用。便于你更好的理解下面的内容。

在平日的开发中,我们用后台写好给我们接口去获取数据。虽然我们有一些请求接口的工具,可以快速的拿到返回数据。但是在一些异常情况的处理上就不太方便了。我列出以下几个痛点:

  • 快速的查看返回数据与数据的处理。(一般我们都是将写好的代码跑到手机上,点击到对应页面的对应按钮)

  • 异常信息的返回与处理。比如一个接口返回一个列表数据,如果列表为空呢?(找后台给我们模拟数据)网速不好呢?(不知道怎么搞…)网络异常呢?(关闭网络)

  • 后台有部分接口没有写好,你就只能等他了。

不知道上面的这三点有没有戳到你的痛处。如果扎心了,那么老铁你就有必要掌握今天的内容。

1.请求接口

我们就使用网络请求三件套(retrofit + okhttp + rxjava2)来举例。

首先添加一下依赖,同时记得加网络权限。

  //RxJavacompile 'io.reactivex.rxjava2:rxjava:2.1.7'//RxAndroidcompile 'io.reactivex.rxjava2:rxandroid:2.0.1'//okhttpcompile "com.squareup.okhttp3:okhttp:3.9.1"//Retrofitcompile ("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 {@Beforepublic void setUp() {ShadowLog.stream = System.out;initRxJava2();}private void initRxJava2() {RxJavaPlugins.reset();RxJavaPlugins.setIoSchedulerHandler(new Function<Scheduler, Scheduler>() {@Overridepublic Scheduler apply(Scheduler scheduler) throws Exception {return Schedulers.trampoline();}});RxAndroidPlugins.reset();RxAndroidPlugins.setMainThreadSchedulerHandler(new Function<Scheduler, Scheduler>() {@Overridepublic Scheduler apply(Scheduler scheduler) throws Exception {return Schedulers.trampoline();}});}@Testpublic void getUserTest() {GithubService.createGithubService().getUser("simplezhli").subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<User>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(User user) {assertEquals("唯鹿", user.name);assertEquals("http://blog.csdn.net/qq_17766199", user.blog);}@Overridepublic void onError(Throwable e) {Log.e("Test", e.toString());}@Overridepublic void onComplete() {}});}
}

上面的代码中,因为网络请求是异步的,所以我们直接测试是不能直接拿到数据,因此无法打印出Log以及测试。所以我们使用initRxJava2()方法将异步转化为同步。这样我们就可以看到返回信息。测试结果如下:

上面的例子为了简单直观的说明,所以将请求接口的方法写到了测试类中,实际中我们可以Mock方法所在的类直接调用请求方法。配合Robolectric对View控件的状态进行测试。

如果你觉得每次测试都要加initRxJava2这段方法很麻烦,你可以抽象出来,或者使用@Rule

public class RxJavaRule implements TestRule {@Overridepublic Statement apply(final Statement base, Description description) {return new Statement() {@Overridepublic void evaluate() throws Throwable {RxJavaPlugins.reset();RxJavaPlugins.setIoSchedulerHandler(new Function<Scheduler, Scheduler>() {@Overridepublic Scheduler apply(Scheduler scheduler) throws Exception {return Schedulers.trampoline();}});RxAndroidPlugins.reset();RxAndroidPlugins.setMainThreadSchedulerHandler(new Function<Scheduler, Scheduler>() {@Overridepublic 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;}@Overridepublic 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;@Rulepublic RxJavaRule rule = new RxJavaRule();@Beforepublic void setUp() throws URISyntaxException {ShadowLog.stream = System.out;//定义Http Client,并添加拦截器OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).addInterceptor(new MockInterceptor("json数据"))//<-- 添加拦截器.build();//设置Http ClientRetrofit retrofit = new Retrofit.Builder().baseUrl(GithubApi.BASE_URL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();mockGithubService = retrofit.create(GithubApi.class);}@Testpublic void getUserTest() throws Exception {mockGithubService.getUser("weilu") //<-- 这里传入错误的用户名.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<User>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(User user) {assertEquals("唯鹿", user.name);assertEquals("http://blog.csdn.net/qq_17766199", user.blog);}@Overridepublic void onError(Throwable e) {Log.e("Test", e.toString());}@Overridepublic 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;@Rulepublic RxJavaRule rule = new RxJavaRule();@Beforepublic void setUp(){ShadowLog.stream = System.out;// 创建一个 MockWebServerserver = new MockWebServer();//设置响应,默认返回http code是 200MockResponse 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);}@Testpublic void getUserTest() throws Exception {//请求不变mockGithubService.getUser("simplezhli").subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<User>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(User user) {assertEquals("唯鹿", user.name);assertEquals("http://blog.csdn.net/qq_17766199", user.blog);}@Overridepublic void onError(Throwable e) {Log.e("Test", e.toString());}@Overridepublic 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() {@Overridepublic 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

通过上面的例子,是不是可以很好的解决你的痛点,希望对你有帮助。只要我们有和后台有开发文档,约定好数据格式与字段名,那么我们可以更敏捷的去做我们的开发。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/422360.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LC 对角线遍历

LC 对角线遍历 题目描述&#xff1a; 给你一个大小为 m x n 的矩阵 mat &#xff0c;请以对角线遍历的顺序&#xff0c;用一个数组返回这个矩阵中的所有元素。 题目实例&#xff1a; 示例一&#xff1a; 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[…

PaddleOCR封装,在线服务化部署实战(python部署,超新手教程)

OCR&#xff0c;即光学字符识别&#xff08;Optical Character Recognition&#xff09;&#xff0c;是一种将图像中的文字转换为机器编码文字的技术。这种技术可以识别和转换各种来源的文本&#xff0c;包括扫描文档、照片中的文字、手写笔记等。光学字符识别&#xff08;OCR&…

B站免费1080P清晰度

免费解锁B站1080P清晰度的视频 扫码登录后即可解锁1080P清晰度 复制想看视频的链接 登录成功后&#xff0c;把需要下载的链接粘贴到网站的搜索框中&#xff0c;点下搜索按钮&#xff0c;就能自动解析视频的下载地址。 有的链接可以直接解析出来 然后可以直接预览或者下载 …

【学习】focal loss 损失函数

focal loss用于解决正负样本的不均衡情况 通常我们需要预测的正样本要少于负样本&#xff0c;正负样本分布不均衡会带来什么影响&#xff1f;主要是两个方面。 样本不均衡的话&#xff0c;训练是低效不充分的。因为困难的正样本数量较少&#xff0c;大部分时间都在学习没有用…

Gradle构建项目报错Unable to start the daemon process

Unable to start the daemon process. This problem might be caused by incorrect configuration of the daemon. For example, an unrecognized jvm option is used. 可能是配置的Java环境太高了&#xff0c;不支持&#xff0c;把java版本降到8就可以了。

Allegro如何导入芯片的Pin Delay?

Allegro在做等长时,需要导入芯片的Pin Delay才能做真正的等长。因为有些芯片内部的引脚本身就是不等长的,例如海思的部分芯片。 那么如何导入芯片的Pin Delay呢? 1、打开约束管理器,点击Properties(属性)→Component(器件)→Pin Properties→General。 在右栏找到芯片U1,…

Spring Boot 4.0:构建云原生Java应用的前沿工具

目录 前言 Spring Boot简介 Spring Boot 的新特性 1. 支持JDK 17 2. 集成云原生组件 3. 响应式编程支持 4. 更强大的安全性 5. 更简化的配置 Spring Boot 的应用场景 1. 云原生应用开发 2. 响应式应用程序 3. 安全性要求高的应用 4. JDK 17的应用 总结 作…

【C++进阶07】哈希表and哈希桶

一、哈希概念 顺序结构以及平衡树中 元素关键码与存储位置没有对应关系 因此查找一个元素 必须经过关键码的多次比较 顺序查找时间复杂度为O(N) 平衡树中为树的高度&#xff0c;即O( l o g 2 N log_2 N log2​N) 搜索效率 搜索过程中元素的比较次数 理想的搜索方法&#xff1a…

【UEFI基础】EDK网络框架(DNS4)

DNS4 DNS4协议说明 IP地址是一串数据&#xff0c;不便记忆。一般用户在使用TCP/IP协议进行通信时也不使用IP地址&#xff0c;而是使用英文和点号组成的字符串&#xff0c;两者的转换通过DNS&#xff08;Domain Name System&#xff09;来完成。 DNS也有v4和v6版本&#xff0…

如何将一个项目整体推送到gitee上以及如何在gitee上新建一个分支,并将一个项目全部推上去

1.如何将一个项目整体推送到gitee上 在Gitee上创建一个新的仓库&#xff0c;或者选择一个已有的仓库。 在本地的项目文件夹中打开终端或命令提示符窗口。 初始化Git仓库。在终端窗口中执行以下命令&#xff1a; git init将项目所有文件添加到Git的暂存区。执行以下命令&#…

枚举问题刷题

考研机试题目中的很多问题往往能通过暴力方法来求解&#xff0c;这些题目并不需要进行过多的思考&#xff0c;而只需枚举所有可能的情况&#xff0c;或者模拟题目中提出的规则&#xff0c;便可以得到解答。虽然说这种方法看上并不高明&#xff0c;但对于一些简单的题目来说却是…

c语言小游戏之扫雷

目录 一&#xff1a;游戏设计理念及思路 二&#xff1a;初步规划的游戏界面 三&#xff1a;开始扫雷游戏的实现 注&#xff1a;1.创建三个文件&#xff0c;test.c用来测试整个游戏的运行&#xff0c;game.c用来实现扫雷游戏的主体&#xff0c;game.h用来函数声明和包含头文…