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

news2024/11/16 11:53:12

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

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

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

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

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

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

怎么用u盘制作pe系统启动盘

PE系统是一种小型的windows系统&#xff0c;通俗的说法也就是在电脑出现问题不能正常进入系统时的一种紧急备用系统。它容量小能量大&#xff0c;可以解决win系统中经常遇到的一些问题&#xff0c;对于经常使用电脑的用户来说&#xff0c;制作一个pe系统启动盘放在身边是很有必…

selenium python教程第1章

1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API&#xff0c;让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API&#xff0c;你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的AP…

【基于MATLAB的dijkstra算法】

基于MATLAB的dijkstra算法 %姓名&#xff1a;马伟 %日期&#xff1a;2023年6月七号 %作业&#xff1a;通信网理论&#xff0c;最小路径树D算法 function [distances, paths, tree] dijkstra(graph, startNode)numNodes size(graph, 1);distances inf(1, numNodes);visited …

网络安全怎么学?学习路线资料分享

一.自己对网络安全的理解 安全其实有很多个方向&#xff0c;从大的方面来说&#xff0c;也就是测试和开发。测试&#xff0c;细分下来&#xff0c;又有渗透&#xff08;也就是所谓的web&#xff09;&#xff0c;逆向&#xff08;也就是所谓的二进制&#xff0c;主要是代码审计方…

YUM报错No module named yum处理

一、问题描述 某次GreenPlum集群部署过程中&#xff0c;现场人员反馈&#xff0c;yum命令无法使用了&#xff0c;执行报错&#xff1a;No module named yum&#xff0c;如下所示&#xff1a; 相关资料&#xff1a;YUM 二、问题分析处理 2.1 YUM的本质 yum命令本质上是属于py…

泛微信创办公平台,低代码构建丰富应用,满足多种需求

信创已经成为了国家的战略规划&#xff0c;自2022年起&#xff0c;国家已全面推动国资企业的信创改造工作&#xff0c;要求到2027年底&#xff0c;对综合办公、经营管理、生产运营等系统实现“应替尽替、能替则替”。其中&#xff0c;门户、OA、邮件、档案、党群、纪检监察等综…

7年时间,从功能测试到测试开发,和大家聊聊如何突破职业瓶颈?少走弯路

突破自己的技术瓶颈并不是一蹴而就&#xff0c;还是需要看清楚一些东西&#xff0c;这里也有一些经验和见解跟大家分享一下。同样是职场人士&#xff0c;我也有我的经历和故事。在工作期间&#xff0c;我有过2年加薪5次的小小“战绩”&#xff08;同期进入公司的员工&#xff0…

28岁,从字节退休了···

大厂一直是每个程序员都向往职业目标&#xff0c;大厂意味着薪资高、福利好、倍有面儿&#xff0c;而且发展空间也大。甚至有人调侃不想进大厂的程序员不是好程序员。 而在网上&#xff0c;也有各个网友分享自己在大厂的经历&#xff0c;在某平台还有一个近2600万浏览的话题&a…

度晓晓再战高考:百度“AI伙伴”助阵,人均学霸时代来了

6月7日&#xff0c;高考首日&#xff0c;AI 挑战高考语文的新闻刷屏。 在一场关于高考直播的中&#xff0c;百度搜索正在内测的“AI 伙伴”现场挑战高考语文考试&#xff0c;包括作文、微写作、古诗词赏析、文言文翻译、阅读填空等题型。一篇文言文作文甚至迷惑了资深语文老师庄…

永善公司招聘网络红人主播,高薪招募线上策略娱乐主播

永善公司招聘网络红人主播&#xff0c;高薪招募线上策略娱乐主播#主播#高薪职业#直播 招聘 公司直招网络主播多名&#xff01; 直播平台&#xff1a;抖音、酷狗 不收任何费用 没有经验也没有关系只要你有梦想&#xff0c;只要你肯努力并 且有一颗想发大财的野心&#xff…

父母在家千万注意别打开“共享屏幕”,银行卡里的钱一秒被转走......

打开屏幕共享&#xff0c;差点直接被转账 今天和爸妈聊天端午回家的事情&#xff0c;突然说到最近AI诈骗的事情&#xff0c;千叮咛万嘱咐说要对方说方言才行&#xff0c;让他们充分了解一下现在骗子诈骗的手段&#xff0c;顺便也找了一下骗子还有什么其他的手段&#xff0c;打…

【Clickhouse】ReplaceingMergeTree引擎final实现合并去重探索 | 京东云技术团队

前言 在OLAP实践中&#xff0c;在有数据更新的场景中&#xff0c;比如存储订单数据&#xff0c;我们经常会用到ReplaceingMergeTree引擎来去重数据&#xff0c;以获取数据的最新状态。但是ReplaceingMergeTree引擎实现数据的去重合并的操作是异步的&#xff0c;这样在实际查询…

人事项目开发记录2

项目构建 前端项目构建 Vue项目使用webpack来构建。首先确保本地已经安装了NodeJS&#xff0c;然后在CMD中执行如下命令&#xff0c;可以创建并启动一个名为vuehr的前端项目&#xff1a; 在执行“vue init webpack vuehr”命令时&#xff0c;会要求依次输入项目的基本信息&…

核心领域的数字基建梳理

数字基建&#xff1a;新基建的核心 数字基建是数字经济发展的重要底座&#xff0c;《“十四五”数字经济发展规划》的首要重要任务就是“优化升级数字基础设施”&#xff0c;并提出要建设高速泛在、天地一体、云网融合、智能敏捷、绿色低碳、安全可控的智能化综合性数字信息基…

如何有效避免项目需求蔓延?

如何有效避免项目需求蔓延&#xff1f;6种措施&#xff1a; 1、确定项目范围 &#xff08;1&#xff09;确定范围基线 在项目目标明确的基础上&#xff0c;所有干系人在完全了解项目需求后&#xff0c;建立需求范围基线&#xff08;项目范围说明书、工作分解结构WBS、WBS字典&a…

关于矿井地面电力综合自动化系统的研究与产品选型

摘要&#xff1a;煤矿供电系统是煤矿生产的重要动力保障 , 一旦电力中断 , 生产将被迫停止 , 同时停电后容易发生瓦斯积聚爆炸、淹井等恶性事故&#xff0c;现有配电室采用不同厂商的保护装 置产品&#xff0c;没有形成有效的监控配电系统&#xff0c;不便于管理和实现无人值守…

CSS中scope和scoped区别

前言 在css的发展中&#xff0c;涌现了大量的新的特性和专有名词。 scope scope 是 CSS 中的一个伪类选择器&#xff0c;表示当前规则所在元素&#xff0c;它可以用于限定元素选择器的范围。在常规的 CSS 中&#xff0c;所有的选择器都是全局的&#xff0c;即它们适用于文档…

Mybatis的parameterType造成线程阻塞问题分析 | 京东云技术团队

一、前言 最近在新发布某个项目上线时&#xff0c;每次重启都会收到机器的 CPU 使用率告警&#xff0c;查看对应监控&#xff0c;持续时长达 5 分钟&#xff0c;对于服务重启有很大风险。而该项目有非常多 Consumer 消费&#xff0c;服务启动后会有大量线程去拉取消息处理逻辑…

【网络安全】学习路线和资料分享

一.自己对网络安全的理解 安全其实有很多个方向&#xff0c;从大的方面来说&#xff0c;也就是测试和开发。测试&#xff0c;细分下来&#xff0c;又有渗透&#xff08;也就是所谓的web&#xff09;&#xff0c;逆向&#xff08;也就是所谓的二进制&#xff0c;主要是代码审计方…

uniapp(四) 之还原网络请求以及接口封装

通过uniapp官网&#xff0c;不难发现简单的接口请求格式 uni.request({url: https://www.example.com/request, //仅为示例&#xff0c;并非真实接口地址。data: {text: uni.request},header: {custom-header: hello //自定义请求头信息},success: (res) > {console.log(re…