Mock技術的深度理解及WireMock框架基礎使用

一、關鍵概念

  1. spy:監聽調用過程,不具備轉發能力,主要是監聽調用過程(類似抓包,F12的功能)
  2. stub:返回固定值的實現,無法在測試中進行動態變更(指無法根據真實的值進行動態變更),比較死板(類似Charles的map local功能,不經過后端,類似擋板)
  3. proxy:使用代理協議轉發請求并返回真實內容,可以轉發、監聽,甚至修改(類似Charles的rewrite功能,把請求轉發給真實的服務,服務返回response后,對response進行一些修改后轉發給前端)
  4. fake:用假的實現代替真的實現,但是實現中做了些捷徑(比如一個大集群下某個服務出故障,需要很長時間去修復,這個時候可以寫一個簡版的邏輯進行替代,通常是開發做的)
  5. mock:由mock庫動態創建的,能提供類似spy、stub、proxy的功能。mock是一種特殊的fake,強調的是可控
  • mock on stub:直接返回固定值數據
  • mock on proxy:利用代理轉發并修改返回數據
在這里插入圖片描述

二、應用場景

一、stub應用場景:

二、fake應用場景:

  • H2 Database Engine:輕量級的內存Sql,不會占用資源,是JDBC的api,操作方式和mysql之類的一致。如果是試運行階段或者重點關注重點不在db層,可以使用這個框架。

三、mock應用場景:

  • Wire Mock:目前一款較為流行的mock框架,社區很活躍,寫法也很優雅,功能和Charles差不多,但是更靈活,因為可以進行編碼
  • 官網:http://wiremock.org/
  • 下面我們重點介紹下這款框架的基礎配置和使用

1. 引入依賴

  <groupId>org.example</groupId>
    <artifactId>wiremock_demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-runner</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-launcher</artifactId>
            <version>1.7.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-console-standalone</artifactId>
            <version>1.7.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.github.tomakehurst</groupId>
            <artifactId>wiremock-jre8</artifactId>
            <version>2.31.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.lightbody.bmp</groupId>
            <artifactId>browsermob-core</artifactId>
            <version>2.1.5</version>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>4.4.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
                <configuration>
                    <includes>
                        <include>**</include>
                    </includes>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>3.0.0-M5</version>
            </plugin>
        </plugins>
    </build>

2. 基礎demo

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
import org.junit.jupiter.api.Test;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;

public class Demo_01_Base {
    @Test
    public void start() {
        int port = 8089;
        // 實例化wirmockServer對象
        WireMockServer wireMockServer = new WireMockServer(
                wireMockConfig()
                        // 配置端口號
                        .port(port)
                        // 配置全局response模板
                        .extensions(new ResponseTemplateTransformer(true))
        );
        // 啟動mock服務
        wireMockServer.start();
        // 這邊需要再次設置一下端口號,否則會報錯
        configureFor(port);
        // 配置mock服務中的一個stub,類似Charles的mapLocal功能
        stubFor(get(urlEqualTo("/some/thing"))
                // 配置返回的body,header,statusCode
                .willReturn(
                        aResponse()
                                .withStatus(200)
                                .withHeader("content-Type", "application/json")
                                .withBody("this is my first wiremock demo!")
                ));
        System.out.println("http://localhost:" + port);
        // 等待10s,如果不等待就會直接停止服務,就啟動不了了
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // wiremock復位
        WireMock.reset();
        // wiremock停止(不停止下一次就無法進行調用了)
        wireMockServer.stop();
    }
}

3. 常用的請求體的配置

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
import org.junit.jupiter.api.Test;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;

public class Demo_02_RequestMatching {
    @Test
    public void start() {
        int port = 8090;
        // 實例化wirmockServer對象
        WireMockServer wireMockServer = new WireMockServer(
                wireMockConfig()
                        // 配置端口號
                        .port(port)
                        // 配置全局response模板
                        .extensions(new ResponseTemplateTransformer(true))
        );
        // 啟動mock服務
        wireMockServer.start();
        // 這邊需要再次設置一下端口號,否則會報錯
        configureFor(port);
        /**
         *   配置mock服務中的一個stub,類似Charles的mapLocal功能
         *   這些配置是“且”的關系,必須全部滿足,才能請求成功
         */

        // url的路徑等于"everything"’
        stubFor(any(urlPathEqualTo("everything"))
                //通過header匹配規則
                .withHeader("Accept", containing("xml"))
                //通過cookie匹配規則
                .withCookie("session", matching(".*12345.*"))
                //通過QueryParam匹配規則
                .withQueryParam("search_term", equalTo("WireMock"))
                //通過withBasicAuth匹配規則
                .withBasicAuth("jeff@example.com", "jeffteenjefftyjeff")
                //通過RequestBody匹配規則
                .withRequestBody(matchingJsonPath("$.a", equalTo("1")))
                .willReturn(aResponse().withStatus(200)
                        .withHeader("Content-Type", "text/plain")
                        .withBody("pass!")));
        System.out.println("http://localhost:" + port);
        // 等待10s,如果不等待就會直接停止服務,就啟動不了了
        try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // wiremock復位
        WireMock.reset();
        // wiremock停止(不停止下一次就無法進行調用了)
        wireMockServer.stop();
    }
}


做個測試

public class Demo_02_Test_RequestMatching {
    @Test
    void testRequestMatching() {
        given().log().all()
                .auth().preemptive().basic("jeff@example.com", "jeffteenjefftyjeff")
                .header("Accept", "xml")
                .cookie("session", "123456")
                .body("{\"a\":1,\"b\":2}")
                .queryParam("search_term", "WireMock")
                .when()
                .post("http://localhost:8099/everything").
                then().log().all()
                .extract();
    }
}

請求成功!


在這里插入圖片描述
  • 查看wireMock的管理后臺:ip:port/__admin
    在這里插入圖片描述

    4. 常用的響應體的配置
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
import org.junit.jupiter.api.Test;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;

/**
 * 常用的響應的配置
 */
public class Demo_03_Response {
    @Test
    public void start() {
        int port = 8091;
        // 實例化wirmockServer對象
        WireMockServer wireMockServer = new WireMockServer(
                wireMockConfig()
                        .port(port)
                        .extensions(new ResponseTemplateTransformer(true))
        );
        // 啟動mock服務
        wireMockServer.start();
        configureFor(port);
        stubFor(get(urlEqualTo("/some/thing"))
                // 配置返回的body,header,statusCode
                .willReturn(
                        aResponse()
                                .withStatus(200)
                                .withHeader("content-Type", "application/json")
                                .withBody("this is my first wiremock demo!")
                ));
        // ok()表示返回響應狀態碼為200
        stubFor(delete("/fine")
                .willReturn(ok()));
        // 返回響應狀態碼+body
        stubFor(get("/fineWithBody")
                .willReturn(ok("body")));
        // 返回響應體為json格式
        stubFor(get("/returnJson")
                .willReturn(okJson("{\"status\":\"success\"}")));
        // 進行請求重定向
        stubFor(get("/redirect")
                .willReturn(temporaryRedirect("/new/place")));
        // 未鑒權
        stubFor(get("/unauthorized")
                .willReturn(unauthorized()));
        // 配置響應狀態碼
        stubFor(get("/statusCode")
                .willReturn(status(418)));

        System.out.println("http://localhost:" + port);
        try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // wiremock復位
        WireMock.reset();
        // wiremock停止(不停止下一次就無法進行調用了)
        wireMockServer.stop();
    }
}

5. 匹配優先級

/**
 * 優先級匹配:
 * 1,匹配優先級高的
 * 2,再按照url進行匹配
 */
public class Demo_04_Priority {
    @Test
    public void start() {
        int port = 8093;
        // 實例化wirmockServer對象
        WireMockServer wireMockServer = new WireMockServer(
                wireMockConfig()
                        .port(port)
                        .extensions(new ResponseTemplateTransformer(true))
        );
        // 啟動mock服務
        wireMockServer.start();
        configureFor(port);
        // 匹配特定的
        stubFor(get(urlEqualTo("/some/thing")).atPriority(2)
                // 配置返回的body,header,statusCode
                .willReturn(
                        aResponse()
                                .withStatus(200)
                                .withHeader("content-Type", "application/json")
                                .withBody("this is my first wiremock demo!")
                ));
        // 使用正則進行通配
        stubFor(get(urlMatching("/some/.*")).atPriority(12)
                .willReturn(
                        aResponse()
                                .withStatus(401)
                                .withBody("match any")
                ));
        // 兜底邏輯
        stubFor(any(anyUrl()).atPriority(1)
                .willReturn(
                        aResponse()
                                .withStatus(402)
                                .withBody("no match")
                ));
        System.out.println("http://localhost:" + port);
        try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // wiremock復位
        WireMock.reset();
        // wiremock停止(不停止下一次就無法進行調用了)
        wireMockServer.stop();
    }
}

6. 錄制和回放

wireMock也提供界面,進行錄制和回放,功能類似于Charles里面的save response

  • 具體操作:
  1. 輸入網址:http://ip:port/__admin/recorder/

  2. 在這個界面輸入你想要錄制的網址,然后點擊錄制選項

  3. 新開一個頁面,輸入http://ip:port,會自動路由到你輸入的網址,這個時候你可以在頁面進行一些操作

    在這里插入圖片描述

    ps:跑之前切記一定要去resources文件夾里新建mappings文件夾!!否則是無法進行錄制的!!我找了好久才找到這個原因!!

  4. 點擊stop停止錄制


    在這里插入圖片描述

    這個時候你會在resources目錄里面看到mappings文件,里面放著錄制好的請求


    在這里插入圖片描述
  5. 我們可以修改里面的內容

    在這里插入圖片描述

    然后再進行重啟,再去訪問http://ip:port,即可mock成功
    在這里插入圖片描述

  6. proxy:使用代理協議轉發請求并返回真實內容

/**
 * 使用代理協議轉發請求并返回真實內容
 */
public class Demo_05_Proxy {
    @Test
    public void start() {
        int port = 8021;
        // 實例化wirmockServer對象
        WireMockServer wireMockServer = new WireMockServer(
                wireMockConfig()
                        // 配置端口號
                        .port(port)
                        // 配置全局response模板
                        .extensions(new ResponseTemplateTransformer(true),
                                // 新建一個response的處理器
                                new ResponseTransformer() {
                                    // 對response里的文本進行替換
                                    @Override
                                    public Response transform(Request request, Response response, FileSource fileSource, Parameters parameters) {
                                        return Response.Builder.like(response)
                                                .body(response.getBodyAsString().replace("Other Utilities","My proxy Utilities"))
                                                .build();
                                    }

                                    @Override
                                    public String getName() {
                                        return "proxy demo";
                                    }
                                })
        );
        // 啟動mock服務
        wireMockServer.start();
        configureFor(port);
        // 配置mock服務中的一個stub
        stubFor(get(urlMatching("/.*"))
                .willReturn(
                        aResponse()
                                // 設置代理
                                .proxiedFrom("https://httpbin.ceshiren.com")
                ));
        System.out.println("http://localhost:" + port);
        try {
            Thread.sleep(10000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // wiremock復位
        WireMock.reset();
        // wiremock停止(不停止下一次就無法進行調用了)
        wireMockServer.stop();
    }
}

啟動以后,訪問ip:port,修改成功!


在這里插入圖片描述
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 什么是單元測試? 單元測試(又稱為模塊測試, Unit Testing)是針對程序模塊(軟件設計的最小單位)來進行...
    常曉csc閱讀 9,409評論 0 6
  • 原創文章,轉載請注明出處。 在做開發的時候app和api定義好接口文檔后,都是同步進行開發的。app端需要api的...
    多來貓閱讀 5,641評論 0 3
  • 1、什么是接口Mock測試? 應用場景思考?1.在前后端分離的項目中,假如后端代碼還未開發完,前端代碼需要調用后端...
    Coding測試閱讀 1,944評論 0 5
  • Mockito簡介什么是mock?在軟件開發的世界之外, "mock"一詞是指模仿或者效仿。 因此可以將“mock...
    燕京博士閱讀 3,575評論 0 6