What is the Mock

A Mock, as its name implies, is a simulation. Often, the same interface is used to simulate an object instead of a real object, which effectively isolates external dependencies and facilitates testing. Mocks are an important part of front-end development and provide many benefits:

  • Parallel development of the front and back ends
  • Simulate various response values for easy testing
  • Some page layout problems under extreme response values can be detected early

background

Front-end development can be simply divided into three stages: parallel development stage, joint commissioning stage and testing stage. At present, most front-end projects are separated from the front and back ends, and it is inevitable to face the problem of data source in the stage of development and joint investigation.

In the joint commissioning stage, each environment has real data, which is convenient for local debugging. We generally point the interface to the real data source. If there are cross-domain limitations, you can use Charles, Fiddler and other debugging agents to solve the problem, or you can use a local Server:

const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();

app.use('/api', proxy({ target: 'your-api-url'.changeOrigin: true }));
app.listen(3000);

Copy the code

If you’re in parallel development, you’ll need Mock data in one of the following ways:

1, Ajax interception, Fetch request disadvantages: front-end mixed with dirty code; Unable to effectively simulate network conditions.

2. Disadvantages of the local Mock Server: It has many interfaces and is expensive to create and modify.

3. Disadvantages of YApi and Easy Mock interface management platform: lack of flexibility. For example, some configuration information is scattered on different interfaces, which is difficult to centrally manage and costly to modify.

This article takes Swagger, which I have been exposed to a lot, as an example to address the disadvantage of the native Mock Server that requires constant interface creation. Open the Network for the Swagger UI address provided by the back end and find an apI-docs file.

This JSON file contains information about the interface, request method, response format, and so on. As you can imagine, parsing this file is not difficult; the only things that might be troublesome are parsing the response value and casting. If you can synchronize data to the local Mock Server at the right time, you can save a lot of tedious manual labor.

Talk is cheap

1, the target

  • Interface paths correspond to Mock directories for easy lookup and modification
  • Request method as the file name, one method for one file, reduce multiple editing conflicts
  • Using Mock. Js to wrap response values makes it easy to simulate extreme situations

Parse the JSON file

As mentioned earlier, the difficulty of parsing JSON files is mainly the conversion of response value types, which we use one of Easy Mock’s parsing modules to do.

const swaggerParserMock = require('swagger-parser-mock');

const synchronizeSwagger = {
  init({ url, blacklist, outputPath }) {
    this.url = url;
    this.blacklist = blacklist;
    this.outputPath = outputPath;
    this.parse();
  },
  async parse() {
    const { paths } = await swaggerParserMock(this.url);
    this.generate(paths);
    console.log(paths);
  }
}

synchronizeSwagger.init({
  // Swagger api-docs address
  "url": "your-api-docs-url".// Output directory
  "outputPath": "./routes".// Blacklist, skip some apis that do not need to synchronize
  "blacklist": []});Copy the code

Print paths information in the following format:

"/path/foo": {
  "get": {
    "summary": "bar"."responses": {
      "200": {
        "example": "'@string'" // The module does the type conversion and mock.js wrapper for us.}}},"post": {
    "summary": "baz"."responses": {
      "200": {
        "example": "'@string'"}}}}Copy the code

3. Traverse the interface. We can add blacklists and filter out interfaces that are not useful to the front end. Reduce interference and improve maintainability.

const fs = require('fs');
const { join } = require('path');
const { promisify } = require('util');
const mkdirp = require('mkdirp');

const writeFile = promisify(fs.writeFile);
const mkdir = promisify(mkdirp);

const synchronizeSwagger = {
  // Iterate over the API path information
  traverse(paths) {
    for (let path in paths) {
      if (this.blacklist.includes(path)) {
        continue;
      }

      for (let method in paths[path]) {
        const pathInfo = paths[path][method];

        if(! pathInfo['responses'] ['200']) {
          continue;
        }
        this.generate(path, method, pathInfo); }}}}Copy the code

4. Generate Mock files to add comments and other information.

const synchronizeSwagger = {
  // Generate mock files
  async generate(path, method, pathInfo) {
    const outputPath = join(__dirname, this.outputPath, path);
    const {
      summary,
      responses: { 200: responseOK },
    } = pathInfo;

    try {
      // Generate a directory
      await mkdir(outputPath);

      const example = responseOK['example'];
      // Generate file contents
      const template = this.generateTemplate({
        summary,
        example,
        method,
        path,
      });

      // Generate files, skip existing files, avoid overwriting local and edited files
      const fPath = join(outputPath, `${method}.js`);
      await writeFile(fPath, template, { flag: 'wx' });
      console.log('Add Mock files:${fPath}`);
    } catch (error) {
      /* eslint-disable no-empty */
    }
  },

  generateTemplate({ summary, example, method, path }) {
  // prettier-ignore
  // Change the {petId} form in API path to :petId
  return ` / * *${summary}
**/
const Mock = require("mockjs");
module.exports = function (app) {
  app.${method}('/api${path.replace(/\{([^}]*)\}/g.: "$1")}', (req, res) => {
    res.json(Mock.mock(${example})); }); }; `; }},Copy the code

5. Start the Mock Server

Using express as an example, we use the Require dynamic feature to create a route that maps to the interface file we just created.

const fs = require('fs');
const join = require('path').join;
const express = require('express');

const app = express();
const port = process.env.PORT || 3000;

app.listen(port, function() {
  console.log(`server is listening ${port}`);
});

function scan(path, app) {
  const files = fs.readdirSync(path);

  for (let i = 0; i < files.length; i++) {
    const fpath = join(path, files[i]);
    const stats = fs.statSync(fpath);

    if (stats.isDirectory()) {
      scan(fpath, app);
    }
    if (stats.isFile()) {
      require(fpath)(app);
    }
  }
}

scan(join(__dirname, './routes'), app);
Copy the code

Write in the last

At this point we use Swagger UI to synchronize Mock data, and if we add cORS, Body-Parser, and other Middleware, a native Mock Server is basically formed. For easy synchronization, we added it to NPM scripts.

  "scripts": {
    "ss": "node ./synchronizeSwagger.js"
  },
Copy the code

Execute NPM run SS to generate the Mock data and access interface.

Attachment: sample code