今天想给大家介绍下我在工作中用到的两个工具,并用这两个工具来快速搭建一个可视化的 Http Mock 平台。一个工具是 WireMock:这个工具相信大家应该不会陌生,这里也就不在过多的进行赘述,没有接触过得请自行百度,相信对你得测试工作会有所帮助。第二个工具是 renren-fast:renren-fast 是一个轻量级的 Spring Boot 快速开发平台,能快速开发项目并交付项目。俨然是代码偏弱得小伙伴得福音,可以大大降低代码编写量,提升开发效率。对于测试来说可以更为简便得开发一个属于自己得平台。这个工具定位是为了方便开发各种后台管理系统,当然你想要使用,还是需要一定的代码能力的,所以该补的基础还是要补的!~

平台搭建思路如下和部分关键代码如下 (demo 地址:https://gitee.com/meng_zi_meng_dad/mock_demo_vue):
1、开发环境搭建,下载 renren-fast 前后端代码、代码创建器,创建数据库,并完成调试启动。请参考以下文档:
--https://www.oschina.net/p/renren-security-boot?hmsr=aladdin1e1
--https://www.renren.io/guide/#end
2、在 pom 中增加 wiremock 相关的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-wiremock</artifactId>
</dependency>
<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock-standalone</artifactId>
    <version>2.19.0</version>
</dependency>

3、编写代码,项目启动后启动 WireMock Client(命名为 MockRunner.java 存放位置为 io.renren.common.utils 下)


package io.renren.common.utils;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.common.FileSource;
import com.github.tomakehurst.wiremock.common.SingleRootFileSource;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import io.renren.modules.sys.service.SysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.io.File;

@Component
public class MockRunner implements ApplicationRunner {

    @Autowired
    private SysConfigService sysConfigService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
         //wiremock文件存放根目录
        FileSource filesRoot = new SingleRootFileSource("D:\\wiremock");
        File path =  new File(sysConfigService.getValue("wireMockFilesRoot")+File.separator+"mappings");
        if (!path.exists() || !path.isDirectory()){
            path.mkdirs();
        }
        WireMockServer wireMockServer;wireMockServer = new WireMockServer(WireMockConfiguration.options()
                .bindAddress("127.0.0.1").port(9090).fileSource(filesRoot));
        wireMockServer.start();
        System.out.println("----------------------------------------WireMock启动成功-------------------------------------------");
    }
}

4、设计 mock 存储的表结构,用代码生成器生成代码 (renren-fast 自带),并加入到前后端代码中,并完善页面显示

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `mcok_case_mapping`
-- ----------------------------
DROP TABLE IF EXISTS `mcok_case_mapping`;
CREATE TABLE `mcok_case_mapping` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `test_case_id` bigint(20) NOT NULL,
  `uuid` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Table structure for `mock_case`
-- ----------------------------
DROP TABLE IF EXISTS `mock_case`;
CREATE TABLE `mock_case` (
  `uuid` varchar(255) DEFAULT NULL,
  `response` longtext,
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `case_name` varchar(255) DEFAULT NULL,
  `case_url` longtext,
  `has_header` bit(1) NOT NULL,
  `header` text,
  `status` bigint(20) NOT NULL,
  `request_data` longtext,
  `request_data_type` varchar(255) DEFAULT NULL,
  `request_method` varchar(255) DEFAULT NULL,
  `visible` int(11) NOT NULL,
  `from_case` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=19879 DEFAULT CHARSET=utf8;

5、增加 WireMock 相关的接口,生成 mock 接口

package io.renren.modules.mock.controller;

import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.http.HttpHeader;
import com.github.tomakehurst.wiremock.http.HttpHeaders;
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import com.google.gson.Gson;
import io.renren.common.utils.Constant;
import io.renren.common.utils.PageUtils;
import io.renren.common.utils.R;
import io.renren.modules.mock.entity.MockCaseEntity;
import io.renren.modules.mock.service.MockCaseService;
import io.renren.modules.sys.service.SysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.github.tomakehurst.wiremock.client.WireMock.*;


/**
 *
 * @date 2019-11-26 16:11:19
 */
@RestController
@RequestMapping("mock/mockcase")
public class MockCaseController extends Constant {
    @Autowired
    private MockCaseService mockCaseService;

    @Autowired
    private SysConfigService sysConfigService;

    /**
     * 列表
     */
    @RequestMapping("/list")
    public R list(@RequestParam Map<String, Object> params){
        PageUtils page = mockCaseService.queryPage(params);

        return R.ok().put("page", page);
    }

    /**
     * 信息
     */
    @RequestMapping("/info/{id}")
    public R info(@PathVariable("id") Long id){
        MockCaseEntity mockCase = mockCaseService.getById(id);

        return R.ok().put("mockCase", mockCase);
    }

    /**
     * 保存
     */
    @RequestMapping("/save")
    public R save(@RequestBody MockCaseEntity mockCase){
        mockCase.setVisible(VISIBLE);
        //调用wiremock相关接口

        if(mockCase.getUuid()!=null && mockCase.getUuid().contains("-")){
            File file = new File(sysConfigService.getValue("wireMockFilesRoot")+File.separator+"mappings");
            File[] mappings =file.listFiles();
            List<File> mapping = Arrays.stream(mappings).filter(p->p.getAbsolutePath().contains(mockCase.getUuid())).collect(Collectors.toList());
            if(mapping.size()>0){
                mapping.get(0).delete();
            }
        }

        configureFor(sysConfigService.getValue("wireMockAddress"), Integer.parseInt(sysConfigService.getValue("wireMockPort")));
        HttpHeaders headers = new HttpHeaders();
        if(mockCase.getHasHeader()!=null && mockCase.getHasHeader() &&  mockCase.getHeader().split("\\|\\|").length>0){
            for (String temp:mockCase.getHeader().split("\\|\\|")
                 ) {
                HttpHeader header = new HttpHeader(temp.split("\\|")[0],temp.split("\\|")[1]);
                headers.plus(header);
            }
        }else{
            mockCase.setHasHeader(false);
            mockCase.setHeader("");
        }
        StubMapping stubMapping  = null;
        if (mockCase.getRequestMethod().equalsIgnoreCase(GET)){
            stubMapping = stubFor(get(urlEqualTo(mockCase.getCaseUrl()))
                    .willReturn(aResponse()
                            .withHeaders(headers)
                            .withStatus(mockCase.getStatus())
                            .withBody(mockCase.getResponse())));
        }else{
            if(mockCase.getRequestDataType().equalsIgnoreCase(JSON)){
                stubMapping = stubFor(post(urlEqualTo(mockCase.getCaseUrl())).withRequestBody(equalToJson(mockCase.getRequestData()))
                        .willReturn(aResponse()
                                .withStatus(mockCase.getStatus())
                                .withBody(mockCase.getResponse())));
            }
            if(mockCase.getRequestDataType().equalsIgnoreCase(FORM_DATA)){
                Map<String, String> map = new Gson().fromJson(mockCase.getRequestData(), Map.class);
                String temp = ".*";
                for (String key : map.keySet()) {
                    temp += key + ".*" + map.get(key).replaceAll("\\{", "\\\\{").replaceAll("\\}", "\\\\}")
                            + ".*";
                }
                stubMapping = stubFor(post(urlEqualTo(mockCase.getCaseUrl())).withRequestBody(matching(temp))
                        .willReturn(aResponse()
                                .withStatus(mockCase.getStatus())
                                .withBody(mockCase.getResponse())));
            }
        }
        mockCase.setUuid(stubMapping.getUuid().toString());
        mockCaseService.saveOrUpdate(mockCase);
        WireMock.saveAllMappings();
        return R.ok();
    }

    /**
     * 删除
     */
    @RequestMapping("/delete")
    public R delete(@RequestBody Long[] ids){
        for (Long id:ids
             ) {
            File file = new File(sysConfigService.getValue("wireMockFilesRoot")+File.separator+"mappings");
            File[] mappings =file.listFiles();
            List<File> mapping = Arrays.stream(mappings).filter(p->p.getAbsolutePath().contains(mockCaseService.getById(id).getUuid())).collect(Collectors.toList());
            if(mapping.size()>0){
                mapping.get(0).delete();
            }
        }
        mockCaseService.removeByIds(Arrays.asList(ids));
        return R.ok();
    }

}

效果如下:




↙↙↙阅读原文可查看相关链接,并与作者交流