zoukankan      html  css  js  c++  java
  • OkHttp3系列(二)MockWebServer使用

    OkHttp3是由Square贡献的HTTP客户端框架,主要用在Andorid中,但是由于其易用的API、强大的功能、请求的快速等特点,也被大量采用在后端开发领域。本系列文章讲述OkHttp3的基本使用、OkHttp3的高级功能以及OkHttp3源码的解析等,请持续关注。


    本篇文章是此系列的第二篇。

    Mock

    mock在测试领域是很重要的一个概念。mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,创建用一个虚拟的对象以方便测试的测试方法。比如在Java中可以借助JMockEasyMock等工具创建Java对象,帮助我们快速进行单元测试。

    MockWebServer

    MockWebServer则是OkHttp3提供的一个快速创建HTTP服务端的工具。当我们的服务需要依赖外部HTTP应用时,可以按照预期功能快速构建外部HTTP应用,加快开发流程,快速进行单元测试,完善代码。搭配OkHttp3使用时,可以测试我们自己编写的OkHttp3客户端代码。

    目前Java版本的MockWebServer最后版本的Maven坐标如下,本编文章的代码示例均基于该版本。

    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>mockwebserver</artifactId>
        <version>3.14.9</version>
    </dependency>
    

    使用MockWebServer

    基本示例

    MockWebServer的使用很简单。

    首先创建一个MockWebServer对象。

    MockWebServer server = new MockWebServer();
    

    然后创建响应内容。

     MockResponse mockResponse = new MockResponse().setBody("hello, world!")
    

    把响应内容放入MockWebServer对象。

    server.enqueue(mockResponse);
    

    启动MockWebServer

    try {
        server.start(8080);
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    高级功能

    模拟弱网环境响应。

    MockWebServer server = new MockWebServer();
    String filePath = "C:\Users\weegee\Downloads\dm-algo-top10.pdf";
    Buffer bodyBuffer = new Buffer();
    bodyBuffer.readFrom(new FileInputStream(new File(filePath)));
    MockResponse bigMockResponse = new MockResponse()
            .addHeader("Cache-Control", "no-cache")
            .setBody(bodyBuffer);
    
    bigMockResponse.setBodyDelay(5, TimeUnit.SECONDS);
    bigMockResponse.throttleBody(1024 * 1024, 1, TimeUnit.SECONDS);
    server.enqueue(bigMockResponse);
    try {
        server.start(8099);
    } catch (IOException e) {
        e.printStackTrace();
        System.exit(0);
    }
    

    throttleBody(long, long, TimeUnit):MockResponse方法的第一个参数是传输字节数,第二个参数是传输第一个参数指定的字节数需要的时间,第三个参数是时间单位。

    上述示例模拟每秒仅能传输1MB字节数据的网络状况,响应体大小为4.2MB,同时设置响应延迟5秒才可开始处理。则该请求正常情况下应该需要9秒多才可以结束,我们用Postman作为请求客户端模拟这一情况。

    服务端请求分发

    正常的HTTP请求一般会对不同路径的请求返回不同的结果,MockWebServer通过Dispatcher支持该功能。

    MockWebServer server = new MockWebServer();
    final Dispatcher dispatcher = new Dispatcher() {
    
        @Override
        public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
    
            if (request.getPath().equals("/v1/login/auth/")){
                return new MockResponse().setResponseCode(200);
            } else if (request.getPath().equals("/v1/check/version")){
                return new MockResponse().setResponseCode(200).setBody("version=9");
            } else if (request.getPath().equals("/v1/profile/info")) {
                return new MockResponse().setResponseCode(200).setBody("{\"info\":{\"name":"Lucas Albuquerque","age":"21","gender":"male"}}");
            }
            return new MockResponse().setResponseCode(404);
        }
    };
    server.setDispatcher(dispatcher);
    
    try {
        server.start(8099);
    } catch (IOException e) {
        e.printStackTrace();
        System.exit(0);
    }
    
    

    上述示例设置了三个请求路径/v1/login/auth//v1/check/version/v1/profile/info,客户端对对应路径的请求返回对应的结果。

    注意: setDispatcherenqueue不能同时使用,若是先使用enqueue再使用setDispatcher,则enqueue的响应体则会丢失,而先setDispatcherenqueue则运行失败,抛出异常java.lang.ClassCastException。原因在于MockWebServer对于所有的响应体MockResponse都是通过一个分发器处理,下面截取MockWebServer的部分源码进行分析。

    // 唯一分发器
    private Dispatcher dispatcher = new QueueDispatcher();
    
    // enqueue方法
    public void enqueue(MockResponse response) {
        ((QueueDispatcher)this.dispatcher).enqueueResponse(response.clone());
    }
    
    // setDispatcher方法,调用该方法会把之前dispatcher内容抛弃,使用方法参数中的dispatcher覆盖原来的内容
    public void setDispatcher(Dispatcher dispatcher) {
        if (dispatcher == null) {
            throw new NullPointerException();
        } else {
            this.dispatcher = dispatcher;
        }
    }
    

    enqueuesetDispatcher比较好理解,但是先setDispatcherenqueue时,发生了以下异常。

    Exception in thread "main" java.lang.ClassCastException: class com.github.chengtengfei.http.OkHttp3MockWebServer$1 cannot be cast to class okhttp3.mockwebserver.QueueDispatcher 
    

    异常发生的位置跟踪到是。

    ((QueueDispatcher)this.dispatcher).enqueueResponse(response.clone());
    

    也就是说,调用setDispatcher方法后,this.dispatcher就不再是QueueDispatcher类型的了,而变成了com.github.chengtengfei.http.OkHttp3MockWebServer$1这个类型。

    一般类名加$1表明当前类中存在内部匿名类,所以编译后以数字代替类名称,反编译OkHttp3MockWebServer$1可获得以下内容。

    class OkHttp3MockWebServer$1 extends Dispatcher {
        OkHttp3MockWebServer$1() {
        }
    
        public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
            if (request.getPath().equals("/v1/login/auth/")) {
                return (new MockResponse()).setResponseCode(200);
            } else if (request.getPath().equals("/v1/check/version")) {
                return (new MockResponse()).setResponseCode(200).setBody("version=9");
            } else {
                return request.getPath().equals("/v1/profile/info") ? (new MockResponse()).setResponseCode(200).setBody("{\"info\":{\"name":"Lucas Albuquerque","age":"21","gender":"male"}}") : (new MockResponse()).setResponseCode(404);
            }
        }
    }
    

    调用setDispatcher前,创建了dispatcher对象,该对象是一个内部类的对象,该内部类继承自okhttp3.mockwebserver.Dispatcher然后实现了dispatch(RecordedRequest):MockResponse方法,方法体中实现了自定义的分发机制,调用setDispatcher传入的参数类型是内部类的类型,和原来的QueueDispatcher不是同一类型,所以进行强制类型转换就会抛出异常。

    请求记录

    MockWebServer支持对客户端的每次请求进行记录,借助该功能可以观察请求时携带的参数、请求头等内容。
    这里我们以前一小节的请求分发为例构建服务端,然后打印每次请求的路径。

    MockWebServer server = new MockWebServer();
    final Dispatcher dispatcher = new Dispatcher() {
    
        @Override
        public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
    
            if (request.getPath().equals("/v1/login/auth")){
                return new MockResponse().setResponseCode(200);
            } else if (request.getPath().equals("/v1/check/version")){
                return new MockResponse().setResponseCode(200).setBody("version=9");
            } else if (request.getPath().equals("/v1/profile/info")) {
                return new MockResponse().setResponseCode(200).setBody("{\"info\":{\"name":"Lucas Albuquerque","age":"21","gender":"male"}}");
            }
            return new MockResponse().setResponseCode(404);
        }
    };
    server.setDispatcher(dispatcher);
    
    try {
        server.start(8099);
    } catch (IOException e) {
        e.printStackTrace();
        System.exit(0);
    }
    
    while (true) {
        RecordedRequest request = server.takeRequest();
        System.out.println(request.getPath());
    }
    

    小结

    本片文章介绍了OkHttp3提供的MockWebServer功能,并详细介绍了它的使用。下篇文章将会介绍一款由笔者打造的用来快速使用OkHttp3的框架,敬请期待。

  • 相关阅读:
    fpm 打包教程
    fpm 打包工具安装调试
    kubernetes 容器挂载 ceph rbd 卷的平滑扩容方法
    kubernetes ceph-rbd挂载步骤 类型PersistentVolume
    ceph 块设备
    kubernetes 外部访问集群暴露端口服务
    kubernetes job的原理
    国内GIT托管服务
    Euclidean Space
    Struct Member Default Value
  • 原文地址:https://www.cnblogs.com/weegee/p/13410049.html
Copyright © 2011-2022 走看看