umbraco-msw-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Umbraco MSW Testing

Umbraco MSW 测试

What is it?

是什么?

MSW (Mock Service Worker) enables testing Umbraco backoffice extensions by intercepting API calls and returning mock responses. This is ideal for testing error states, loading states, and edge cases without a running Umbraco instance.
MSW(Mock Service Worker)通过拦截API请求并返回模拟响应,实现对Umbraco后台扩展的测试。这种方式非常适合在无需运行Umbraco实例的情况下,测试错误状态、加载状态和边缘场景。

When to Use

适用场景

  • Testing API error handling (404, 500, validation errors)
  • Testing loading spinners and skeleton states
  • Testing network retry behavior
  • Testing edge cases without backend setup
  • Adding API mocking to unit tests
  • 测试API错误处理(404、500、验证错误)
  • 测试加载动画和骨架屏状态
  • 测试网络重试行为
  • 无需后端环境即可测试边缘场景
  • 在单元测试中添加API模拟

Related Skills

相关技能

  • umbraco-testing - Master skill for testing overview
  • umbraco-unit-testing - Unit testing patterns (combine with MSW)
  • umbraco-testing - 测试概览的核心技能
  • umbraco-unit-testing - 单元测试模式(可与MSW结合使用)

Documentation

文档

  • MSW Docs: https://mswjs.io/docs/
  • Reference handlers:
    Umbraco-CMS/src/Umbraco.Web.UI.Client/src/mocks/handlers/

  • MSW 文档https://mswjs.io/docs/
  • 参考处理器
    Umbraco-CMS/src/Umbraco.Web.UI.Client/src/mocks/handlers/

Setup

配置步骤

Dependencies

依赖安装

Add to
package.json
:
json
{
  "devDependencies": {
    "@open-wc/testing": "^4.0.0",
    "@web/dev-server-esbuild": "^1.0.0",
    "@web/dev-server-import-maps": "^0.2.0",
    "@web/test-runner": "^0.18.0",
    "@web/test-runner-playwright": "^0.11.0",
    "msw": "^2.7.0"
  },
  "scripts": {
    "postinstall": "npx msw init . --save",
    "test": "web-test-runner",
    "test:watch": "web-test-runner --watch"
  }
}
Then run:
bash
npm install
npx playwright install chromium
The
postinstall
script copies
mockServiceWorker.js
to your project root. Without this file, MSW will fail silently.
package.json
中添加以下内容:
json
{
  "devDependencies": {
    "@open-wc/testing": "^4.0.0",
    "@web/dev-server-esbuild": "^1.0.0",
    "@web/dev-server-import-maps": "^0.2.0",
    "@web/test-runner": "^0.18.0",
    "@web/test-runner-playwright": "^0.11.0",
    "msw": "^2.7.0"
  },
  "scripts": {
    "postinstall": "npx msw init . --save",
    "test": "web-test-runner",
    "test:watch": "web-test-runner --watch"
  }
}
然后运行:
bash
npm install
npx playwright install chromium
postinstall
脚本会将
mockServiceWorker.js
复制到项目根目录。如果没有这个文件,MSW会静默失败。

Configuration

配置文件

Create
web-test-runner.config.mjs
:
javascript
import { esbuildPlugin } from '@web/dev-server-esbuild';
import { playwrightLauncher } from '@web/test-runner-playwright';
import { importMapsPlugin } from '@web/dev-server-import-maps';

export default {
  rootDir: '.',
  files: ['./src/**/*.test.ts', '!**/node_modules/**'],
  nodeResolve: {
    exportConditions: ['development'],
    preferBuiltins: false,
    browser: false,
  },
  browsers: [playwrightLauncher({ product: 'chromium' })],
  plugins: [
    importMapsPlugin({
      inject: {
        importMap: {
          imports: {
            '@umbraco-cms/backoffice/external/lit': '/node_modules/lit/index.js',
            '@umbraco-cms/backoffice/lit-element':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/packages/core/lit-element/index.js',
            '@umbraco-cms/backoffice/element-api':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/libs/element-api/index.js',
            '@umbraco-cms/backoffice/observable-api':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/libs/observable-api/index.js',
            '@umbraco-cms/backoffice/context-api':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/libs/context-api/index.js',
            '@umbraco-cms/backoffice/controller-api':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/libs/controller-api/index.js',
            '@umbraco-cms/backoffice/class-api':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/packages/core/class-api/index.js',
          },
        },
      },
    }),
    esbuildPlugin({
      ts: true,
      tsconfig: './tsconfig.json',
      target: 'auto',
      json: true,
    }),
  ],
  testRunnerHtml: (testFramework) =>
    `<html lang="en-us">
      <head>
        <meta charset="UTF-8" />
        <!-- Load MSW v2 as IIFE to get window.MockServiceWorker -->
        <script src="/node_modules/msw/lib/iife/index.js"></script>
      </head>
      <body>
        <script type="module" src="${testFramework}"></script>
      </body>
    </html>`,
};
创建
web-test-runner.config.mjs
javascript
import { esbuildPlugin } from '@web/dev-server-esbuild';
import { playwrightLauncher } from '@web/test-runner-playwright';
import { importMapsPlugin } from '@web/dev-server-import-maps';

export default {
  rootDir: '.',
  files: ['./src/**/*.test.ts', '!**/node_modules/**'],
  nodeResolve: {
    exportConditions: ['development'],
    preferBuiltins: false,
    browser: false,
  },
  browsers: [playwrightLauncher({ product: 'chromium' })],
  plugins: [
    importMapsPlugin({
      inject: {
        importMap: {
          imports: {
            '@umbraco-cms/backoffice/external/lit': '/node_modules/lit/index.js',
            '@umbraco-cms/backoffice/lit-element':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/packages/core/lit-element/index.js',
            '@umbraco-cms/backoffice/element-api':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/libs/element-api/index.js',
            '@umbraco-cms/backoffice/observable-api':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/libs/observable-api/index.js',
            '@umbraco-cms/backoffice/context-api':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/libs/context-api/index.js',
            '@umbraco-cms/backoffice/controller-api':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/libs/controller-api/index.js',
            '@umbraco-cms/backoffice/class-api':
              '/node_modules/@umbraco-cms/backoffice/dist-cms/packages/core/class-api/index.js',
          },
        },
      },
    }),
    esbuildPlugin({
      ts: true,
      tsconfig: './tsconfig.json',
      target: 'auto',
      json: true,
    }),
  ],
  testRunnerHtml: (testFramework) =>
    `<html lang="en-us">
      <head>
        <meta charset="UTF-8" />
        <!-- Load MSW v2 as IIFE to get window.MockServiceWorker -->
        <script src="/node_modules/msw/lib/iife/index.js"></script>
      </head>
      <body>
        <script type="module" src="${testFramework}"></script>
      </body>
    </html>`,
};

Directory Structure

目录结构

my-extension/
├── src/
│   ├── my-element.ts
│   ├── my-element.test.ts
│   └── mocks/
│       ├── handlers.ts         # MSW handlers
│       ├── setup.ts            # Worker setup
│       └── data/
│           └── items.db.ts     # Mock database
├── mockServiceWorker.js        # Generated by postinstall
├── web-test-runner.config.mjs
├── package.json
└── tsconfig.json

my-extension/
├── src/
│   ├── my-element.ts
│   ├── my-element.test.ts
│   └── mocks/
│       ├── handlers.ts         # MSW处理器
│       ├── setup.ts            # Worker配置
│       └── data/
│           └── items.db.ts     # 模拟数据库
├── mockServiceWorker.js        # 由postinstall生成
├── web-test-runner.config.mjs
├── package.json
└── tsconfig.json

Patterns

模式

MSW v2 Syntax

MSW v2 语法

Umbraco uses MSW v2. Key API patterns:
ConceptMSW v2 Syntax
HTTP methods
http.get()
,
http.post()
,
http.put()
,
http.delete()
JSON response
HttpResponse.json(data)
Status codes
HttpResponse.json(data, { status: 201 })
Empty response
new HttpResponse(null, { status: 204 })
Request params
({ params }) => { ... }
Request body
({ request }) => { const body = await request.json(); }
Delay
await delay(2000)
Umbraco使用MSW v2。核心API模式如下:
概念MSW v2 语法
HTTP方法
http.get()
,
http.post()
,
http.put()
,
http.delete()
JSON响应
HttpResponse.json(data)
状态码
HttpResponse.json(data, { status: 201 })
空响应
new HttpResponse(null, { status: 204 })
请求参数
({ params }) => { ... }
请求体
({ request }) => { const body = await request.json(); }
延迟
await delay(2000)

Global MSW Access

全局MSW访问

typescript
const { http, HttpResponse, delay } = window.MockServiceWorker;
typescript
const { http, HttpResponse, delay } = window.MockServiceWorker;

umbracoPath Helper

umbracoPath 工具函数

typescript
import { umbracoPath } from '@umbraco-cms/backoffice/utils';

// Creates: /umbraco/management/api/v1/document/:id
umbracoPath('/document/:id')
typescript
import { umbracoPath } from '@umbraco-cms/backoffice/utils';

// 生成路径: /umbraco/management/api/v1/document/:id
umbracoPath('/document/:id')

Basic Handlers

基础处理器

GET Handler:
typescript
const { http, HttpResponse } = window.MockServiceWorker;
import { umbracoPath } from '@umbraco-cms/backoffice/utils';

export const handlers = [
  http.get(umbracoPath('/document/:id'), ({ params }) => {
    const id = params.id as string;
    return HttpResponse.json({
      id,
      name: 'Test Document',
      documentType: { alias: 'testType' },
    });
  }),
];
POST Handler:
typescript
http.post(umbracoPath('/document'), async ({ request }) => {
  const body = await request.json();

  if (!body.name) {
    return HttpResponse.json(
      {
        type: 'validation',
        status: 400,
        errors: { name: ['Name is required'] },
      },
      { status: 400 }
    );
  }

  const newId = crypto.randomUUID();
  return HttpResponse.json(
    { id: newId },
    {
      status: 201,
      headers: { 'Umb-Generated-Resource': newId },
    }
  );
}),
PUT Handler:
typescript
http.put(umbracoPath('/document/:id'), async ({ params, request }) => {
  const id = params.id as string;
  const body = await request.json();
  mockDb.update(id, body);
  return new HttpResponse(null, { status: 200 });
}),
DELETE Handler:
typescript
http.delete(umbracoPath('/document/:id'), ({ params }) => {
  const id = params.id as string;
  mockDb.delete(id);
  return new HttpResponse(null, { status: 200 });
}),
GET处理器:
typescript
const { http, HttpResponse } = window.MockServiceWorker;
import { umbracoPath } from '@umbraco-cms/backoffice/utils';

export const handlers = [
  http.get(umbracoPath('/document/:id'), ({ params }) => {
    const id = params.id as string;
    return HttpResponse.json({
      id,
      name: 'Test Document',
      documentType: { alias: 'testType' },
    });
  }),
];
POST处理器:
typescript
http.post(umbracoPath('/document'), async ({ request }) => {
  const body = await request.json();

  if (!body.name) {
    return HttpResponse.json(
      {
        type: 'validation',
        status: 400,
        errors: { name: ['Name is required'] },
      },
      { status: 400 }
    );
  }

  const newId = crypto.randomUUID();
  return HttpResponse.json(
    { id: newId },
    {
      status: 201,
      headers: { 'Umb-Generated-Resource': newId },
    }
  );
}),
PUT处理器:
typescript
http.put(umbracoPath('/document/:id'), async ({ params, request }) => {
  const id = params.id as string;
  const body = await request.json();
  mockDb.update(id, body);
  return new HttpResponse(null, { status: 200 });
}),
DELETE处理器:
typescript
http.delete(umbracoPath('/document/:id'), ({ params }) => {
  const id = params.id as string;
  mockDb.delete(id);
  return new HttpResponse(null, { status: 200 });
}),

Simulating States

模拟状态

Error Responses:
typescript
// 404 Not Found
http.get(umbracoPath('/document/:id'), ({ params }) => {
  const doc = mockDb.read(params.id as string);
  if (!doc) return new HttpResponse(null, { status: 404 });
  return HttpResponse.json(doc);
}),

// 500 Server Error
http.get(umbracoPath('/document/:id'), () => {
  return HttpResponse.json(
    { type: 'error', detail: 'Internal server error' },
    { status: 500 }
  );
}),
Validation Errors:
typescript
http.post(umbracoPath('/document'), async ({ request }) => {
  const body = await request.json();
  if (!body.name) {
    return HttpResponse.json(
      {
        type: 'validation',
        errors: {
          name: ['Name is required'],
          title: ['Title must be at least 3 characters'],
        },
      },
      { status: 400 }
    );
  }
  return new HttpResponse(null, { status: 201 });
}),
Delayed Responses (Loading States):
typescript
http.get(umbracoPath('/slow-endpoint'), async () => {
  await delay(2000);
  return HttpResponse.json({ data: 'loaded' });
}),
错误响应:
typescript
// 404 未找到
http.get(umbracoPath('/document/:id'), ({ params }) => {
  const doc = mockDb.read(params.id as string);
  if (!doc) return new HttpResponse(null, { status: 404 });
  return HttpResponse.json(doc);
}),

// 500 服务器错误
http.get(umbracoPath('/document/:id'), () => {
  return HttpResponse.json(
    { type: 'error', detail: 'Internal server error' },
    { status: 500 }
  );
}),
验证错误:
typescript
http.post(umbracoPath('/document'), async ({ request }) => {
  const body = await request.json();
  if (!body.name) {
    return HttpResponse.json(
      {
        type: 'validation',
        errors: {
          name: ['Name is required'],
          title: ['Title must be at least 3 characters'],
        },
      },
      { status: 400 }
    );
  }
  return new HttpResponse(null, { status: 201 });
}),
延迟响应(加载状态):
typescript
http.get(umbracoPath('/slow-endpoint'), async () => {
  await delay(2000);
  return HttpResponse.json({ data: 'loaded' });
}),

Mock Database Pattern

模拟数据库模式

typescript
// src/mocks/data/items.db.ts
interface Item {
  id: string;
  name: string;
  value: number;
}

class ItemsMockDb {
  private data: Item[] = [
    { id: '1', name: 'Item 1', value: 100 },
    { id: '2', name: 'Item 2', value: 200 },
  ];

  read(id: string) {
    return this.data.find((item) => item.id === id);
  }

  readAll() {
    return [...this.data];
  }

  create(item: Omit<Item, 'id'>) {
    const newItem = { ...item, id: crypto.randomUUID() };
    this.data.push(newItem);
    return newItem.id;
  }

  update(id: string, updates: Partial<Item>) {
    const index = this.data.findIndex((i) => i.id === id);
    if (index !== -1) {
      this.data[index] = { ...this.data[index], ...updates };
    }
  }

  delete(id: string) {
    this.data = this.data.filter((i) => i.id !== id);
  }
}

export const itemsDb = new ItemsMockDb();
typescript
// src/mocks/data/items.db.ts
interface Item {
  id: string;
  name: string;
  value: number;
}

class ItemsMockDb {
  private data: Item[] = [
    { id: '1', name: 'Item 1', value: 100 },
    { id: '2', name: 'Item 2', value: 200 },
  ];

  read(id: string) {
    return this.data.find((item) => item.id === id);
  }

  readAll() {
    return [...this.data];
  }

  create(item: Omit<Item, 'id'>) {
    const newItem = { ...item, id: crypto.randomUUID() };
    this.data.push(newItem);
    return newItem.id;
  }

  update(id: string, updates: Partial<Item>) {
    const index = this.data.findIndex((i) => i.id === id);
    if (index !== -1) {
      this.data[index] = { ...this.data[index], ...updates };
    }
  }

  delete(id: string) {
    this.data = this.data.filter((i) => i.id !== id);
  }
}

export const itemsDb = new ItemsMockDb();

Worker Setup

Worker配置

typescript
// src/mocks/setup.ts
const { setupWorker } = window.MockServiceWorker;
import { handlers } from './handlers.js';

const worker = setupWorker(...handlers);

export const startMockServiceWorker = () =>
  worker.start({
    onUnhandledRequest: 'warn',
    quiet: true,
  });
typescript
// src/mocks/setup.ts
const { setupWorker } = window.MockServiceWorker;
import { handlers } from './handlers.js';

const worker = setupWorker(...handlers);

export const startMockServiceWorker = () =>
  worker.start({
    onUnhandledRequest: 'warn',
    quiet: true,
  });

Integration with Tests

与测试集成

In test file:
typescript
import { expect, fixture } from '@open-wc/testing';
import { startMockServiceWorker } from './mocks/setup.js';
import './my-element.js';

// Start MSW before tests
before(async () => {
  await startMockServiceWorker();
});

describe('MyElement with API', () => {
  it('displays data from API', async () => {
    const element = await fixture(html`<my-element></my-element>`);
    await element.updateComplete;

    // Element should show mocked data
    expect(element.shadowRoot?.textContent).to.include('Item 1');
  });
});

在测试文件中:
typescript
import { expect, fixture } from '@open-wc/testing';
import { startMockServiceWorker } from './mocks/setup.js';
import './my-element.js';

// 在测试前启动MSW
before(async () => {
  await startMockServiceWorker();
});

describe('MyElement with API', () => {
  it('displays data from API', async () => {
    const element = await fixture(html`<my-element></my-element>`);
    await element.updateComplete;

    // 元素应显示模拟数据
    expect(element.shadowRoot?.textContent).to.include('Item 1');
  });
});

Examples

示例

Complete Handler File

完整处理器文件

typescript
// src/mocks/handlers.ts
const { http, HttpResponse } = window.MockServiceWorker;
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
import { itemsDb } from './data/items.db.js';

export const handlers = [
  // List items
  http.get(umbracoPath('/my-extension/items'), () => {
    const items = itemsDb.readAll();
    return HttpResponse.json({ total: items.length, items });
  }),

  // Get single item
  http.get(umbracoPath('/my-extension/items/:id'), ({ params }) => {
    const item = itemsDb.read(params.id as string);
    if (!item) return new HttpResponse(null, { status: 404 });
    return HttpResponse.json(item);
  }),

  // Create item
  http.post(umbracoPath('/my-extension/items'), async ({ request }) => {
    const body = await request.json();
    if (!body.name) {
      return HttpResponse.json(
        { type: 'validation', errors: { name: ['Required'] } },
        { status: 400 }
      );
    }
    const id = itemsDb.create(body);
    return HttpResponse.json(
      { id },
      {
        status: 201,
        headers: { 'Umb-Generated-Resource': id },
      }
    );
  }),

  // Update item
  http.put(umbracoPath('/my-extension/items/:id'), async ({ params, request }) => {
    const id = params.id as string;
    if (!itemsDb.read(id)) return new HttpResponse(null, { status: 404 });
    itemsDb.update(id, await request.json());
    return new HttpResponse(null, { status: 200 });
  }),

  // Delete item
  http.delete(umbracoPath('/my-extension/items/:id'), ({ params }) => {
    const id = params.id as string;
    if (!itemsDb.read(id)) return new HttpResponse(null, { status: 404 });
    itemsDb.delete(id);
    return new HttpResponse(null, { status: 200 });
  }),
];
typescript
// src/mocks/handlers.ts
const { http, HttpResponse } = window.MockServiceWorker;
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
import { itemsDb } from './data/items.db.js';

export const handlers = [
  // 列出所有条目
  http.get(umbracoPath('/my-extension/items'), () => {
    const items = itemsDb.readAll();
    return HttpResponse.json({ total: items.length, items });
  }),

  // 获取单个条目
  http.get(umbracoPath('/my-extension/items/:id'), ({ params }) => {
    const item = itemsDb.read(params.id as string);
    if (!item) return new HttpResponse(null, { status: 404 });
    return HttpResponse.json(item);
  }),

  // 创建条目
  http.post(umbracoPath('/my-extension/items'), async ({ request }) => {
    const body = await request.json();
    if (!body.name) {
      return HttpResponse.json(
        { type: 'validation', errors: { name: ['Required'] } },
        { status: 400 }
      );
    }
    const id = itemsDb.create(body);
    return HttpResponse.json(
      { id },
      {
        status: 201,
        headers: { 'Umb-Generated-Resource': id },
      }
    );
  }),

  // 更新条目
  http.put(umbracoPath('/my-extension/items/:id'), async ({ params, request }) => {
    const id = params.id as string;
    if (!itemsDb.read(id)) return new HttpResponse(null, { status: 404 });
    itemsDb.update(id, await request.json());
    return new HttpResponse(null, { status: 200 });
  }),

  // 删除条目
  http.delete(umbracoPath('/my-extension/items/:id'), ({ params }) => {
    const id = params.id as string;
    if (!itemsDb.read(id)) return new HttpResponse(null, { status: 404 });
    itemsDb.delete(id);
    return new HttpResponse(null, { status: 200 });
  }),
];

Handler Organization

处理器组织方式

src/mocks/
├── handlers.ts             # Aggregates all handlers
├── setup.ts                # Worker setup
├── handlers/
│   ├── document.handlers.ts
│   ├── media.handlers.ts
│   └── my-extension.handlers.ts
└── data/
    ├── document.db.ts
    └── items.db.ts
typescript
// handlers.ts
import { documentHandlers } from './handlers/document.handlers.js';
import { mediaHandlers } from './handlers/media.handlers.js';
import { myExtensionHandlers } from './handlers/my-extension.handlers.js';

export const handlers = [
  ...documentHandlers,
  ...mediaHandlers,
  ...myExtensionHandlers,
];

src/mocks/
├── handlers.ts             # 聚合所有处理器
├── setup.ts                # Worker配置
├── handlers/
│   ├── document.handlers.ts
│   ├── media.handlers.ts
│   └── my-extension.handlers.ts
└── data/
    ├── document.db.ts
    └── items.db.ts
typescript
// handlers.ts
import { documentHandlers } from './handlers/document.handlers.js';
import { mediaHandlers } from './handlers/media.handlers.js';
import { myExtensionHandlers } from './handlers/my-extension.handlers.js';

export const handlers = [
  ...documentHandlers,
  ...mediaHandlers,
  ...myExtensionHandlers,
];

Running Tests

运行测试

bash
undefined
bash
undefined

Run all tests

运行所有测试

npm test
npm test

Run in watch mode

以监听模式运行

npm run test:watch
npm run test:watch

Run specific file

运行指定文件

npx web-test-runner src/my-element.test.ts

---
npx web-test-runner src/my-element.test.ts

---

Troubleshooting

故障排除

MSW not intercepting requests

MSW未拦截请求

  1. Check
    mockServiceWorker.js
    exists in project root
  2. Verify MSW script is loaded in test HTML:
    <script src="/node_modules/msw/lib/iife/index.js"></script>
  3. Ensure worker is started before tests run
  1. 检查项目根目录是否存在
    mockServiceWorker.js
  2. 验证测试HTML中是否加载了MSW脚本:
    <script src="/node_modules/msw/lib/iife/index.js"></script>
  3. 确保在测试运行前启动了Worker

"http is not defined"

"http is not defined"

Use global access:
const { http, HttpResponse } = window.MockServiceWorker;
使用全局访问方式:
const { http, HttpResponse } = window.MockServiceWorker;

Handler not matching

处理器不匹配

Check path matches exactly. Use
umbracoPath()
for Umbraco API paths.
检查路径是否完全匹配。对于Umbraco API路径,使用
umbracoPath()
生成。

Requests still hitting real server

请求仍访问真实服务器

Ensure
onUnhandledRequest: 'warn'
is set to see unhandled requests in console.

确保设置了
onUnhandledRequest: 'warn'
,以便在控制台中查看未被处理的请求。

Migration from MSW v1

从MSW v1迁移

If upgrading from MSW v1, here are the key changes:
MSW v1MSW v2
rest.get()
http.get()
rest.post()
http.post()
(req, res, ctx) => res(ctx.json(data))
() => HttpResponse.json(data)
res(ctx.status(404))
new HttpResponse(null, { status: 404 })
res(ctx.delay(2000), ctx.json(data))
await delay(2000); return HttpResponse.json(data)
req.params.id
({ params }) => params.id
await req.json()
({ request }) => await request.json()
如果从MSW v1升级,以下是关键变更:
MSW v1MSW v2
rest.get()
http.get()
rest.post()
http.post()
(req, res, ctx) => res(ctx.json(data))
() => HttpResponse.json(data)
res(ctx.status(404))
new HttpResponse(null, { status: 404 })
res(ctx.delay(2000), ctx.json(data))
await delay(2000); return HttpResponse.json(data)
req.params.id
({ params }) => params.id
await req.json()
({ request }) => await request.json()