Mock技术的深度理解及Wire Mock框架基础使用

一、关键概念

  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:利用代理转发并修改返回数据

Mock技术的深度理解及Wire Mock框架基础使用

二、应用场景

一、stub应用场景:

  • Moco:https://github.com/dreamhead/moco
  • 轻量级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();
    }
}

请求成功!
Mock技术的深度理解及Wire Mock框架基础使用

  • 查看wireMock的管理后台:ip:port/__admin
    Mock技术的深度理解及Wire Mock框架基础使用
    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,会自动路由到你输入的网址,这个时候你可以在页面进行一些操作
    Mock技术的深度理解及Wire Mock框架基础使用

  4. 点击stop停止录制
    Mock技术的深度理解及Wire Mock框架基础使用
    这个时候你会在resources目录里面看到mapping文件,里面放着录制好的请求
    Mock技术的深度理解及Wire Mock框架基础使用

  5. 我们可以修改里面的内容
    Mock技术的深度理解及Wire Mock框架基础使用
    然后再进行重启,再去访问http://ip:port,即可mock成功
    Mock技术的深度理解及Wire 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,修改成功!
Mock技术的深度理解及Wire Mock框架基础使用

上一篇:mock项目中使用


下一篇:mock.js的使用