@ codewits/ openapi- msw
📖 完整文档
@codewithagents/openapi-msw 从您的 OpenAPI 3. 1 个 spec 生成 MSW v2 模拟处理器, 并带有种子 Faker 数据: 将其指向一个 spec, 获得一个类型安全 handlers.ts 您可以立即跳入浏览器工作室或节点模拟服务器.
- 每次行动一名操作员: 每一个
get, (中文(简体) ),post, (中文(简体) ),put, (中文(简体) ),patch,以及delete操作成为http.<method>(path, resolver)导出条目handlers阵列. - 种子、决定性的嘲弄数字 :
faker.seed(<seed>)(默认)42)被写入生成的文件,因此每个运行都具有相同的光谱和种子产生相同的模拟值. 改变种子,再生,获得一个新的但仍然稳定的数据集. - MSW v2 路径语法: 打开API
{id}路径参数被重写为MSW 结肠样式:id。路径中的每个参数都会转换(/orgs/{orgId}/repos/{repoId}变成/orgs/:orgId/repos/:repoId) (中文(简体) ). - 在浏览器和节点工作: 处理器路径被固定在一个源(
*默认通配符,或您的base_url),因此它们与绝对的URL相匹配fetch发送下setupServer和浏览器请求setupWorker。 。 。 . - 知会身体:每个处理器返回一个由操作的 2xx JSON 响应计划所塑造的由 Faker 生成的正体, 荣誉字符串格式, enums, 阵列, 和嵌入对象.
- 瞄准内置固定器数字 :
fixtures: true还写fixtures.ts,一个键入a<Name>(overrides?)内置,每个测试处理器每个组件的构建器。 见 固定建筑工。 。 。 . - 高级清洁输出: 生成
handlers.ts通过prettier --check从盒子里出来. - 打开API 3.x:3.1.x是首要目标;3.0x在实践中得到支持(13个显示规格中的8个是3.0.x,全部编译在下)
tsc --strict) (中文(简体) ).
安装
pnpm add -D @codewithagents/openapi-msw
# or
npm install -D @codewithagents/openapi-msw
生成的 handlers.ts 从 msw 财务报告和审定财务报表 @faker-js/faker 直接,所以两者都是 同行依赖关系 你安装自己:
pnpm add msw @faker-js/faker
| 同伴依赖关系 | 范围 |
|---|---|
msw | ^2.0.0 || ^3.0.0 |
@faker-js/faker | ^10.5.0 |
此软件包取决于 openapi-zod-ts 用于 spec 解析和共享配置管道,但不要求您生成模型或客户端。 单独运行 或与其余的套房.
快开始
1. 创建 openapi-msw.config.json 在您的工程根中 :
{
"input_openapi": "./spec/api.json",
"output": "./src/mocks",
"seed": 42,
"max_array_items": 3,
"depth_cap": 30
}
仅 input_openapi 财务报告和审定财务报表 output 需要这样做。 三个数值字段是可选的并默认为上述数值.
2. 运行生成器:
npx openapi-msw
# or point at a config elsewhere:
npx openapi-msw --config ./config/openapi-msw.config.json
CLI没有立场上的论点. 可用旗帜为 --config <path>, (中文(简体) ), --help 编号 : -h,以及 --version 编号 : -v时 --config ,在配置文件目录中的配置解析度中的相对路径.
3个. handlers.ts 在输出目录中显示 :
这个 output 如果目录不存在,则以递归方式创建。 将处理器接通到MSW:
// browser worker
import { setupWorker } from 'msw/browser'
import { handlers } from './src/mocks/handlers'
export const worker = setupWorker(...handlers)
// Node / test server
import { setupServer } from 'msw/node'
import { handlers } from './src/mocks/handlers'
export const server = setupServer(...handlers)
生成输出
鉴于任务 API GET /api/v1/tasks (返回一页列表)和 DELETE /api/v1/tasks/{id} (204 无内容),生成 handlers.ts 看起来像这样:
// This file is auto-generated by @codewithagents/openapi-msw, do not edit
import { http, HttpResponse } from 'msw'
import { faker } from '@faker-js/faker'
faker.seed(42)
export const handlers = [
http.get('*/api/v1/tasks', () =>
HttpResponse.json({
items: Array.from({ length: 3 }, () => ({
id: faker.string.uuid(),
title: faker.lorem.word(),
status: faker.helpers.arrayElement(['pending', 'in_progress', 'done']),
priority: faker.number.int({ min: 1, max: 1000 }),
assigneeEmail: faker.internet.email(),
createdAt: faker.date.recent().toISOString(),
})),
total: faker.number.int({ min: 1, max: 1000 }),
})
),
http.delete('*/api/v1/tasks/:id', () => HttpResponse.json(null, { status: 204 })),
]
一个承诺的,漂流检查的样本生活在 packages/integration/generated/handlers.ts从 packages/integration/spec/api.json。 。 。 .
配置引用
openapi-msw.config.json数字 :
| 外地 | 需求 | 默认 | 说明 |
|---|---|---|---|
input_openapi | 对 | 无 | OpenAPI 3.x spec( JSON 或 YAML) 路径 |
output | 对 | 无 | 要写的目录 handlers.ts (创建回转) |
seed | 没有 | 42 | 传到 faker.seed() 在生成的文件中。 必须是整数 >= 0 |
max_array_items | 没有 | 3 | 生成模拟中的数组长度 。 必须是整数 >= 1 |
depth_cap | 没有 | 30 | Schema 重现深度后再跳出 null。必须是整数 >= 1 |
base_url | 没有 | * 通配符来源 | 起源(可选带基路径) 被前缀到每个处理器路径上,例如. "https://api.test" 给 https://api.test/api/v1/tasks被丢弃了 必须是非空字符串 |
| fixtures |无| false 时间 true,同时写入 fixtures.ts (每司马修所. 一定是布林尔的..
| models_import |无| "./models.js" 模块指定符 fixtures.ts 导入模型类型 openapi-zod-ts models.ts) (中文(简体) ). 必须是非空字符串 {}
可选密钥仅在存在时才被验证. 每个键,除了 projects 也可以设置在 a 中的每个条目 projects 阵列.
多相相相生成. 将顶级字段替换为 projects 生成一个数组 handlers.ts 光谱。 混合 a projects 带有顶级的阵列 input_openapi 编号 : output 掷出.
{
"projects": [
{ "input_openapi": "./specs/public.json", "output": "./src/mocks/public" },
{ "input_openapi": "./specs/admin.json", "output": "./src/mocks/admin" }
]
}
固定建筑工fixtures: true(中文(简体) )
handlers.ts 财务报告和审定财务报表 fixtures.ts 解决两个不同的问题:
| 文件 | 作用 | 用它来 |
|---|---|---|
handlers.ts | 雾层. 一套完整的处理器,每个操作一个,有一个种子数据集 | Dev 工人(setupWorker,演示后端,或基线 setupServer(...handlers) 每次测试都从 |
fixtures.ts | 每测试数据. 每个组件计划一个建构器,返回完整、有效的值 | 单项测试内置处理器(server.use(...)) 测试控制准确反应 |
宣告内置处理器的组件测试需要图案类型的完整对象. 手写固定装置从光谱上漂移;生成的构造器没有. 打开 "fixtures": true,以及 fixtures.ts 写在 handlers.ts数字 :
import { http, HttpResponse } from 'msw'
import { server } from './mocks/server'
import { aTask } from './mocks/fixtures.js'
it('shows the task title', async () => {
const TASK = aTask({ title: 'Write docs', status: 'done' })
server.use(http.get('*/api/v1/tasks/:id', () => HttpResponse.json(TASK)))
// render, then assert on TASK.title ...
})
每一个计划 components.schemas 生成器会发出 :
export function aTask(overrides?: Partial<Task>, variant = 0): Task
aTask.full = (overrides?: Partial<Task>, variant = 0): Task => ...
- 取名问.
a<Name>,或an<Name>当类型名称以元音开头时(anOwner, (中文(简体) ),anUpdateTaskRequest) (中文(简体) ).<Name>确切的类型名称openapi-zod-ts输出于models.ts。 。 。 . - 和你的模型对打.
fixtures.ts已经import type { ... } from './models.js'(单位:千美元)openapi-zod-ts(产出).overrides实值Partial<Name>,因此一个错误的值或未知的密钥是编译错误。 点models_import在其他地方,当模型不 住在模型旁边,例如."models_import": "../api/models.js"。 。 。 . - 浅色合并.
aTask({ owner })替换owner批发; 不合并为生成owner。 。 。 . - 默认情况下只要求 .
aTask()只填充列表中的属性required(复入入于巢所取之物也.aTask.full()在对象本身和由一个筑巢者级别(在aPet.full()一个巢owner里面也满了,下面的任何东西都是必要的,所以.full()以大而相通的规格为界.writeOnly属性总是被忽略,匹配读形模型类型. - 每个电话都要决定. 每一次呼叫都重新播音一个私人 Faker 实例(与 Global
faker那个handlers.ts(a) 使用)(seed, builder, variant)。同一呼叫总是返回相同的值,不管在它之前是哪个建筑商运行的,还是测试在哪个顺序运行的,所以快照和断言保持稳定。 相对日期(date-time编号 :date字段)根据固定的参考日期计算2025-01-01T00:00:00.000Z)而不是"现在",所以它们也比较平稳地跨越了跑道和天数. 换个variant以获得不同的、仍然具有决定性的价值,例如,在一个清单中有两个不同的项目:[aTask({}, 0), aTask({}, 1)]。 。 。 . - 尼斯特德计划再利用他们的建造者. 一个财产
$refs 另外一个组件计划称为 schema 的构建者(anOwner(...),或anOwner.full(...)内部.full()由调用者的种子Faker所取出的一个变体,所以嵌入值具有确定性,但阵列项目彼此不同fixtures.ts在计划数中保持线性。 返回一个周期的参考文献(例如:TreeNode.children,或A -> B -> A)是被守护的:复发会深入一个巢穴,然后产生null,所以每个建筑工都会终止. - 非对象计划 (enums, 数组, 原始的) 获得一个没有覆盖的数值构建器 :
aTaskStatus(variant?)财务报告和审定财务报表aTaskStatus.full(variant?)。 。 。 . - 价值观是最佳的. 生成的值遵循与 Faker 规则相同的
handlers.ts(见下文);它们被投入模型类型,而不是被证明不利于模型类型,因此,把一个测试取决于其中的值放在其中overrides。 。 。 .
人体如何产生
每个处理器的解析器正体由操作中选择的2xx JSON响应方案来构建. 每个计划节点都选择Faker呼叫:
| 司马 | 生成值 |
|---|---|
string,格式 email | faker.internet.email() |
string,格式 uuid | faker.string.uuid() |
string,格式 date-time | faker.date.recent().toISOString() |
string,格式 date | faker.date.recent().toISOString().slice(0, 10) |
string,格式 uri 编号 : url | faker.internet.url() |
string (其他部分) | faker.lorem.word() |
integer, (中文(简体) ), int32, (中文(简体) ), int64 | faker.number.int({ min: 1, max: 1000 }) |
number (其他部分) | faker.number.float({ min: 0, max: 1000, fractionDigits: 2 }) |
boolean | faker.datatype.boolean() |
enum | faker.helpers.arrayElement([...literals]) |
array | Array.from({ length: max_array_items }, () => item) |
对象为 additionalProperties | Object.fromEntries(...) 包含两个生成的条目 |
null / 无法解决 | null |
非明显行为
- 种子生活在输出中,而不是运行时间.
faker.seed(<seed>)是生成文件的第一个可执行行。 要改变模拟值, 需要以不同的方式重生seed. 两跑相同的谱和种子产生字节-等同处理器. - Handler URL 是源代码选择. 赤裸的相亲者,如
'/api/v1/tasks'由MSW针对location.href,因此在节点(setupServer从msw/node它从不匹配 绝对要求喜欢fetch('http://localhost/api/v1/tasks')因此,生成的处理器总是带有一种来源:'*/api/v1/tasks'默认(MSW的通配符:任何来源、浏览器和节点都一样),或'<base_url>/api/v1/tasks'何时base_url已经设定。 设定base_url当您想要处理器只匹配一个来源时, 或者当您的服务器 URL 带有一个基本路径时, spec'spaths不包括(例如"https://api.test/v2") (中文(简体) ). 在此更改之前产生的输出使用赤裸的相对路径;再生成来取回固定. - 2xx选取顺序. 发电机更喜欢一个
200反应,然后201,然后第一个宣布application/json躯体。 A 类200发射HttpResponse.json(body)没有状态参数; 任何其他 2xx(例如)201发射HttpResponse.json(body, { status: N })。 。 。 . - 没有JSON的尸体. 如果选中的 2xx 响应没有
application/jsonschema, 处理器发出HttpResponse.json(null, { status: N })(从第一个 2xx 代码状态,默认为204) (中文(简体) ). 如果该答复宣布为非JSON内容类型(text/plain, (中文(简体) ),text/csv, (中文(简体) ),application/octet-stream,图像等)处理器发出new HttpResponse(null, { status: N })而不是谎称application/json。仅限application/json内容被假尸体所嘲弄. allOf合并;anyOf编号 :oneOf不属于工会. 全部属性allOf成员合并为一个对象及其required组合数组。 对于anyOf编号 :oneOf生成器选择第一个混凝土(非)$ref成员,或解决第一个成员,如果所有成员都是裁判.depth_cap边界重现. 深度反增量只显示在$ref决议和决定的执行情况allOf编号 :anyOf编号 :oneOf决议步骤。 串联式筑巢和平地财产准入不算数。 当深度超过depth_cap,节点发出null /* depth cap reached */。 。 。 .- 通知通知 完全终止. A 类
$ref返回到已经访问过的计划名称null /* circular ref: depth cap reached */,不同于"深度-盖"的注释,所以自取偏好的策略不会循环. - 类型联盟和缺失类型. 用于 OpenAPI 3.1
type数组,生成器使用第一个元素。 计划没有type不过properties被作为物体对待;否则会发出null。 。 。 . - 道路固守的护栏.
input_openapi财务报告和审定财务报表output解析为系统目录的路径( e)/etc, (中文(简体) ),/usr, (中文(简体) ),/bin, (中文(简体) ),C:\Windows,和类似)作为可能的错配置而被拒绝. 这是护栏 不是安全沙盒.
程序化 API
该套件还输出一个方案面,用于围绕它建立工具:
import {
generateHandlers,
generateFixtures,
generate,
loadConfig,
loadConfigs,
} from '@codewithagents/openapi-msw'
// Build the handlers file in memory from a parsed spec.
const { filename, content } = generateHandlers(spec, {
seed: 42,
maxArrayItems: 3,
depthCap: 30,
baseUrl: 'https://api.test', // optional; omit for the '*' wildcard origin
})
// filename === 'handlers.ts'
// Build fixtures.ts in memory.
const fixtures = generateFixtures(spec, {
seed: 42,
maxArrayItems: 3,
depthCap: 30,
modelsImport: './models.js',
})
// fixtures.filename === 'fixtures.ts'
generate 运行完整的配置驱动流程(装入配置,剖析 spec,写入文件). loadConfig 编号 : loadConfigs 读取并验证配置文件。 导出类型为 MswConfig, (中文(简体) ), HandlerGenOptions, (中文(简体) ), FixtureGenOptions,以及 GeneratedFile。 。 。 .
Openapi-zod-ts套房的一部分
openapi-msw 是其中五个已公布的软件包之一 代码与代理/openapi-zod-ts 单相机 4个生成器从一个 OpenAPI spec 中发送文件,还有一个运行时的帮助者在应用代码中映射 API 错误 :
| 软件包 | 作用 |
|---|---|
openapi-zod-ts | 生成器: TypeScript 模型, 本地获取客户端, 和 Zod schemas |
@codewithagents/openapi-server | 生成器:框架不可知服务接口外加可选 hono | express | fastify | none 路由器( 默认) none(中文(简体) ) |
@codewithagents/openapi-react-query | 生成器: 在生成的客户端上输入回放查询 v5 钩 |
@codewithagents/openapi-msw | 生成器: 这个包, MSW v2 处理器, 有种子 Faker 模拟数据 |
@codewithagents/api-errors | 运行时帮助器:将后端错误响应映射到窗体-字段错误 |
见 完整存储参考应用 (Fastify + React + reaction-query,有浏览器e2e套件),用于这些包一起工作到结束.
许可证
麻省理工学院