WireMock+renren-fast 快速搭建 HTTP Mock 視覺化平臺思路

萌梓萌爸發表於2020-06-03

今天想給大家介紹下我在工作中用到的兩個工具,並用這兩個工具來快速搭建一個視覺化的 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();
    }

}

效果如下:



相關文章