Mocking data is an inevitable requirement in the collaborative development process with separate front and back ends. There are many mature solutions for data mocking in the common http way, but it becomes more complicated for websocket push mode.

Here we share the data request mock solution for websocket services with the help of simple-mock plugin library.

Introduction to simple-mock

In the case that no other roles such as product and back-end are involved and only front-end development is facilitated, I personally prefer a simple and lightweight data MOCK solution that does not invade the front-end project, so I developed simple-mock based on the development pattern of front-end business at work. simple-mock) plug-in library. The solution provided by simple-mock is mainly based on the following requirement points.

  • Can be injected into webpack-dev-server (Vue-cli, Angular-cli) or existing servers such as express, Koa, etc.
    • Proxy the backend service and save the results returned by the interface as the base mock data during the proxy process
    • Write commonjs/esModule style code as js/ts files to customize mock rules.
  • Front-end development can enable mock mode with minimal configuration
  • Ability to load configuration files, custom mock files in real time
  • You can define public rules and design different priorities
  • You can mock some interfaces
  • more…

Can be injected into an existing Server

The initial purpose of simple-mock is to implement Server-level Mock functionality without changing the existing architectural model. So simple-mock implements and exports two methods simpleMock.render and simpleMock.saveApi for users to call, and implements the specific mock logic framework internally.

  • The simpleMock.render method implements the mock functionality. It should be injected at the request of the interface and decide whether to mock the current request and how to handle it according to the rules of the configuration file. The specific mock rules are written in js/ts files to achieve the most familiar commonjs code for front-end, no complicated rules to master, almost zero learning cost for front-end developers.
  • The simpleMock.saveApi method automatically saves the data returned by the server-side interface. It should be called when the server-side API proxy data is returned and decides how to save the returned data according to the configuration file rules. In the current mock schemes, the front-end people are required to make their own mock rules, and the existing rules need to be revised when the interface changes. Here, we reduce the manual mock rule making by automatically saving the returned data by proxy mode, and only focus on the current interface to be processed when mock mode is enabled.

For the other requirements, where there are differences in the project business, the configuration parameters are written in the configuration file to achieve.

Zero/minimal configuration to enable mock mode

Currently, various popular mock solutions require front-end staff to make their own mock rules due to their feature-richness, and the initial cost of introducing them in existing projects is relatively high, and there is also a certain learning cost.

simple-mock saves the returned data according to the configuration file rules by exporting the simpleMock.saveApi method to the user when the server-side API proxy data is returned. By automatically saving the returned data from the backend through the proxy mode, manual mock rule making is reduced, which makes the introduction cost relatively low. You only need to write some public correction rules (if there is no special interface logic or even this step can be omitted), you can quickly realize the complete MCOK development of the whole site.

In the MOCK development process, you only need to focus on the business-related data interfaces currently being processed, and write targeted Javascript logic to achieve custom data MOCK.

Real-time loading of configuration files, custom mock files

The configuration file and the mock file both come in as the nodejs module require, because the require mechanism has a cache, which causes the file to be modified only after a restart to take effect, while simple-mock, which only provides an API as a plugin, obviously does not have the ability to restart the server.

In local development mode, the memory consumption requirement is not too high, so simple-mock implements a simple caching mechanism internally, and removes the cache using the delete require.cache[filename] method when the relevant file is found to be modified (note that this approach may lead to memory leaks unless there is a good object release control logic, not recommended in production environments), thus achieving the effect of require taking effect immediately after the file is modified again.

Define public rules and design different priorities

simple-mock sets up three directories for defining mock rule files, in descending order of priority: customdata > mockdata > customdata/autosave.

  • The customdata directory is used for custom API rules and is not committed to the GIT repository. This directory has the highest priority
  • mockdata directory is used for common public API rules, which are committed to the GIT repository
  • customdata/autosave directory is used to automatically save the returned data from the backend, and is also a guaranteed mock rule

mock for some interfaces

When the mock function is turned off (setting config.isEnableMock=false), the config.enableMockFilter parameter method can be used to define rules that can still be turned on. This is useful for scenarios where MOCK debugging is only done for a single interface with different parameter values. Example.

1
2
3
4
5
6
7
8
module.exports = {
    enableMockFilter: (apiPath, req) => {
        // 示例:按URL 路径匹配,开启部分接口的 mock
        const filterKeyList = ['/rest/test', '/rest/abc'];
        const isMock = filterKeyList.some(path => String(apiPath).includes(path));
        return isMock;
    },
}

When mock is enabled (set config.isEnableMock=true), the config.disableMockFilter parameter method can be used to define rules that still do not use mock. This is useful for scenarios where you need to debug or compare some of the online interfaces in a mock development situation. Example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
module.exports = {
    disableMockFilter: (apiPath, req) => {
        // 示例:按URL关键字过滤,登陆相关API不mock
        const filterKeyList = ['/rest/auth', '/rest/logout'];<img src="https://lzw.me/wp-content/uploads/2020/07/websocket-mock-server-140x100.png" alt="" width="140" height="100" class="aligncenter size-thumbnail wp-image-2542" />
 
<img src="https://lzw.me/wp-content/uploads/2020/07/websocket-mock-msg-send-2-140x100.png" alt="" width="140" height="100" class="aligncenter size-thumbnail wp-image-2541" />
        const isMock = filterKeyList.some(path => String(apiPath).includes(path));
        return isMock;
    },
}

Of course, when the mock switch is turned on, there are various ways to terminate the mock process. For example, matching the relevant rule in the config.customSaveFileName parameter method and returning filename as null, matching the relevant request in the config.handlerBeforeMockSend parameter method and returning __ignore_mock__, exporting the value to __ignore_mock__ in the custom file corresponding to the request, etc. in the custom file corresponding to the request, and exporting the value to __ignore_mock__.

The above briefly describes the main purpose and design idea of simple-mock. For http one-answer requests, simple-mock is a good solution for lightweight mock development patterns.

Ideas and solutions for implementing websocket service mock using simple-mock

For websocket subscription and push mode, we can also reuse the mock idea of simple-mock in http-oriented one-answer mock, but the difference lies in how to save the data of different subscription requests, how to simulate multiple data sending after one subscription, etc.

Simple-mock Basic strategy for handling websocket message mock

Generally speaking, except for the data pushed by the server broadcast mode, for sending requests need to wait for the return of data in common mode, because of the need to distinguish between different data responses, websocket client data sent to the server data returned between the basic need to correlate according to certain field values, based on this correlation rules, you can configure parameters to achieve mock Based on such association rules, mock logic can be implemented by configuring parameters such as file name customization, data generalization, etc., which basically remains unchanged in the specific mock framework. The main points of simple-mock for this logic are.

  • Implementing websocket service mock logic in a one-question, one-answer, and one-question-multiple-answer mode with simple-mock.
    • Define the mock file name rules for client data request and server data return by config.customSaveFileName(req, res, filename, type) parameter method, thus realizing the correspondence between response and answer.
    • The config.handlerBeforeMockSend(content, reqParams) parameter method adjusts generic information, such as real-time timestamp, uuid replacement corresponding to the request, etc.
  • For login process, message preload, etc., define public rules to be placed in the mock/mockdata directory (which will be committed to the git repository)
  • For broadcast push-style messages, define public rules, define timers, etc. It is recommended to manually mock messages and send them by opening a local websocket mock server page after starting a connection

One question, one answer, one question, multiple answer mode

One-Question, One-Answer, One-Question, Many In specific business processes, the one-question, one-answer, one-question, many-answer pattern is still more and more predominant, for example, sending a message, the server correspondingly returns one or more messages.

The sent and returned messages need to be matched, which generally sets a uuid unique identifier field in each answer for matching. The config.handlerBeforeMockSend parameter can be used to handle this public logical relationship, and the config.customSaveFileName parameter is used to handle the correspondence between the request and the local file.

The one-response mode is naturally simpler, with the autosave directory landing data satisfying the basic requirements. The one-response-multiple-answer mode requires specific rules to be implemented in the form of written functions. The following is a custom example of the one-response-multiple-answer model: Answer mode

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
 * 自定义 MOCK 逻辑示例:查询并要求返回多条信息
 */
const path = require('path');
module.exports = (req, client) => {
  const jparams = req.data && req.data.jparams;
  const reqmsgid = jparams && params.reqmsgid;
  const totalCounts = result.data.totalCounts;
  const funcid = result.data.funcid;
  // reqmsgid 是本次请求的 uuid 唯一标识,用于应答匹配
  // 这种公共约定也可以在 config.handlerBeforeMockSend 参数中进行实现
  result.data.reqmsgid = reqmsgid;
 
  // 将多条保存至本地的数据
  if (totalCounts) {
    for (let i = 0; i < totalCounts; i++) {
        const filePath = path.resolve(__dirname, `../customdata/autosave/${funcid}_currSno${i}.js`);
 
        if (fs.existsSync(filePath)) {
            let data = require(filePath);
            data.data.reqmsgid = reqmsgid;
            // 也可以调用 config.handlerBeforeMockSend 进行一下通用处理再发送
            // const CONFIG = require('../../simple-mock-config');
            // data = CONFIG.handlerBeforeMockSend(data, req, client);
 
            setTimeout(() => {
                client.broadcast(data);
                // client.send(data);
            }, 1000);
        }
 
    }
  }
  if (jparams && jparams.market) {
      let s = jparams.market;
      if (s === '1' && jparams.filterstktypes) s += '_filterstktypes';
      const filePath = path.resolve(__dirname, `../customdata/autosave/10001_${s}.js`);
 
      if (fs.existsSync(filePath)) {
          const data = require(filePath);
          // console.log('data', data);
          setTimeout(() => {
              client.broadcast(data);
              // client.send(data);
          }, 100);
      }
  }
  // 返回 false 则忽略自动应答,在内部自行处理
  // return result;
  return false;
};
// 示例基础数据
const result = {
  data: {
    msg: '信息查询成功!',
    errno: 0,
    data: [],
    currSno: 0,
    totalCounts: 10,
    funcid: 10001,
    reqmsgid: '2da60eda555000000000000000000000',
  },
  topic: 'ask',
};

This example shows a scenario where a single request requires multiple messages to be returned. The client is the second parameter passed to the simpleMock.renderWs method, which can be passed directly to the ws client handle, or you can implement its related interface to achieve more customization capabilities.

Login process, information pre-loading

During the initial login process, you may need to load a lot of information. The logic for receiving this information can be fixed as public rules, which are written in the mock/mockdata directory and then committed to the Git repository to be shared with all developers.

In addition, you may need to write public rules for some public messages to be loaded in order for the application to work properly.

Mock Push Messaging

For unsolicited, pure push messages, a simple way is to enable a timer (setInterval/setTimeout) at startup to send messages to the client at regular intervals (pay attention to the management of the timer to avoid memory leaks). For example, read the contents of a json file in a specified directory at regular intervals and send it as a message. Only need to delete the file or modify the content of the file to meet the simple needs.

In the development process, the main purpose of Mock for push messages is for functional debugging, so it is more convenient to open a web client to send mock numbers manually. The following example is shown.

Instant pushing of arbitrary messages via the web client can easily meet the development and debugging needs of various data scenarios. An example implementation of this pattern is given in ws-proxy-server/mock-client.ts of the simple-mock repository. An example implementation of this pattern is given in ws-proxy-server/mock-client.ts.

This implements the basic mock logic for the websocket service. A concrete example is available in the simple-mock source repository ws-proxy-server.

Other

  • If you are not concerned about logging information, you can turn on the log printing switch parameter slient in the configuration file for simple-mock and ws-proxy-server. Because a lot of log printing may cause the cmd to stall or the process may get stuck and require a manual enter to continue.
  • more…