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的框架,敬請期待。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM