MSW 前端 mock 方案

334 阅读3分钟

1357274.png

Why

  • 可以更清晰直观的了解复杂接口数据之间存在的逻辑。在某些情况下(比如 metadata 这样复杂的数据场景下),我们有这份清晰的 mock 数据,会比后端更直观了解到数据之间的逻辑关系。
  • 可保留开发时的 mock 数据,方便后续查看和调试数据。
  • 定义完入参出参后,绝大部分情况下,不需要后端接口即可完成开发。也不需要后端调试时提供非常多的时间支持,甚至在没有接口的情况下,我们获取数据的方式就已经和后端最终返回一模一样。
  • 不需要网络,可离线开发。
  • 代码侵入小,覆盖范围广。打开开关和关闭开关对于业务侧代码,没有任何的感觉。不会太多的代码侵入和改造成本。
  • 点对点直接代理现有的接口。
  • 现有真实接口和 mock 接口可以共存。可以部分指定代理 mock。
  • 修改和设置数据源非常简单。Mock 数据源可以通过复制简单获取最新的数据。修改仅需在对应 json 上做简单的更改保存即可。
  • 多场景模拟。API调用无效、缺少错误处理或收到意外响应,网络慢、大体量数据情况等等。
  • Bug 查找和新功能调试方便。调试直接复制一份现有的相关接口代码,然后进行数据变更调试。
  • Test 支持

步骤

install

yarn add msw --dev

mkdir src/mocks
touch src/mocks/handlers.js

handlers

// src/mocks/handlers.js
import { rest } from 'msw'

export const handlers = [
  rest.post('/login', (req, res, ctx) => {
    // Persist user's authentication in the session
    sessionStorage.setItem('is-authenticated', 'true')

    return res(
      // Respond with a 200 status code
      ctx.status(200),
    )
  }),

  rest.get('/user', (req, res, ctx) => {
    // Check if the user is authenticated in this session
    const isAuthenticated = sessionStorage.getItem('is-authenticated')

    if (!isAuthenticated) {
      // If not authenticated, respond with a 403 error
      return res(
        ctx.status(403),
        ctx.json({
          errorMessage: 'Not authorized',
        }),
      )
    }

    // If authenticated, return a mocked user details
    return res(
      ctx.status(200),
      ctx.json({
        username: 'admin',
      }),
    )
  }),
]

public

// npx msw init <PUBLIC_DIR> --save
npx msw init public/ --save
devServer: {
  // static: 如果 contentBase 不支持,试一试 static
  contentBase: [
    // msw 支持
    path.resolve(__dirname, `../src/mocks/public`)
  ]
}

setup

touch src/mocks/browser.js
// src/mocks/browser.js
import { setupWorker } from 'msw'
import { handlers } from './handlers'

// This configures a Service Worker with the given request handlers.
export const worker = setupWorker(...handlers)

start

// src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

if (process.env.NODE_ENV === 'development') {
  const { worker } = require('./mocks/browser')
  worker.start()
}

ReactDOM.render(<App />, document.getElementById('root'))

success

目录

全局 mocks 配置 | src/mocks

-- mocks  
---- app         mock 的测试入口,方便调试组件时的快速项目启动
---- handlers    聚合项目中需要 mock 的其他目录下 handlers
---- public      msw 的官方文件,需要放到 public
---- webpack     部分 webpack 改造逻辑相关
---- config.ts   
---- index.ts
---- worker.ts   msw 浏览器端的配置

某个模块下使用

模块下数据单独在就近模块下管理。

模块下 Handlers 需要到全局 mocks 中注册。

单个 mock 接口通过 mockApi + xxx 来进行命名,一个文件中尽量一个 mock 接口定义,方便该接口的不同 mock 数据存放,避免混乱。

-- work-content
---- mock
------ mockApp      当前模块下,需要快速调试的独立组件,可能聚合了当下需要调试的各种能力。
                    调试时可以在全局 mocks(src/mocks/app)中进行引入调试。               
------ index.ts     导出模块下需要 mock 的 handlers 配置,提供给全局 mocks handlers(src/mocks/handlers) 中引入注册。
------ mockApiXXX   对应的 mock handlers,mock 某个接口,以 mockApi + xxx 命名。原子到单个接口。