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阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,103评论 18 139
  • 什么是单元测试? 单元测试(又称为模块测试, Unit Testing)是针对程序模块(软件设计的最小单位)来进行...
    常晓csc阅读 9,332评论 0 6
  • 原创文章,转载请注明出处。 在做开发的时候app和api定义好接口文档后,都是同步进行开发的。app端需要api的...
    多来猫阅读 5,512评论 0 3
  • 1、什么是接口Mock测试? 应用场景思考?1.在前后端分离的项目中,假如后端代码还未开发完,前端代码需要调用后端...
    Coding测试阅读 1,801评论 0 5
  • Mockito简介什么是mock?在软件开发的世界之外, "mock"一词是指模仿或者效仿。 因此可以将“mock...
    燕京博士阅读 3,365评论 0 6