1 | ## Mock
|
2 |
|
3 | [![npm version](https://img.shields.io/npm/v/@forchange/mock.svg)](https://www.npmjs.com/package/@forchange/mock)
|
4 |
|
5 | mock 自定义规则、流程的数据模拟服务
|
6 |
|
7 | ## 特点
|
8 |
|
9 | - mock 方式多样化,支持 json 和 function
|
10 | - 隔离接口,容易维护,适合开源协作
|
11 | - 接口联动处理,可根据业务流程自定义返回数据
|
12 | - 文件路径即是路由和方法,接口唯一,更好的开发体验
|
13 | - 支持 webSocket,连接、断开、主动推送消息 等操作可视化
|
14 |
|
15 | ## 快速开始
|
16 |
|
17 | #### 安装
|
18 |
|
19 | **安装 node 环境**
|
20 |
|
21 | ```
|
22 | $ brew install node
|
23 | ```
|
24 |
|
25 | **安装 mock 服务**
|
26 |
|
27 | ```
|
28 | $ npm install -g @forchange/mock
|
29 | ```
|
30 |
|
31 | #### 开始
|
32 |
|
33 | **初始化:**
|
34 |
|
35 | ```
|
36 | $ fmock init
|
37 | ```
|
38 |
|
39 | ```
|
40 | $ cd <输入的文件名>
|
41 | ```
|
42 |
|
43 | **启动:**
|
44 |
|
45 | ```
|
46 | $ fmock
|
47 | ```
|
48 |
|
49 | 现在,访问 http://0.0.0.0:3839/user/info 查看接口
|
50 |
|
51 | ## 如何使用
|
52 |
|
53 | #### 配置 config
|
54 |
|
55 | config 文件是整个服务的配置文件 [详细说明](#config)
|
56 |
|
57 | #### 添加 data
|
58 |
|
59 | data 文件是接口对应的返回数据 [详细说明](#data)
|
60 |
|
61 | #### 添加 rule
|
62 |
|
63 | 当我们构造 data 数据时,将会提供部分 [内部方法](#内部-rule)
|
64 | 当你需要自定义 data 的返回数据时,可通过 rule 文件配置 [详细说明](#自定义-rule)
|
65 |
|
66 | #### 添加 label
|
67 |
|
68 | 为了防止数据文件间的冲突,我们提供 label 标识 [详细说明](#label)
|
69 |
|
70 | #### 添加 flow
|
71 |
|
72 | 当你需要进行接口间的数据联动时,可添加 flow 流程 [详细说明](#flow)
|
73 |
|
74 | #### 示例目录结构
|
75 |
|
76 | ```tree
|
77 | mock
|
78 | ├── config.js //config 配置入口
|
79 | ├── data //data 数据
|
80 | │ └── ambassador
|
81 | │ ├── courses
|
82 | │ │ └── :id
|
83 | │ │ └── :pay#get.js
|
84 | │ ├── courses#get.js
|
85 | │ ├── settlements
|
86 | │ │ └── search.js
|
87 | │ └── settlements#get.js
|
88 | ├── socket //socket 数据
|
89 | │ ├── aaa.js
|
90 | │ ├── access.js
|
91 | │ ├── cun.js
|
92 | │ ├── kkk
|
93 | │ │ └── bbb.js
|
94 | │ └── promise.js
|
95 | └── rule.js //自定义规则
|
96 | ```
|
97 |
|
98 | </br>
|
99 |
|
100 | - data: 添加 [data](#data)
|
101 | - socket: 添加 [socket](#socket)
|
102 | - config.js: 配置 [config](#config)
|
103 | - rule.js: 配置 [自定义规则](#自定义-rule)
|
104 |
|
105 | ## 命令
|
106 |
|
107 | #### 常用指令
|
108 |
|
109 | ```
|
110 | 选项:
|
111 | create <path> [-l <label>] [-m <method>] [-s]
|
112 | path 路径
|
113 | -l, --label label 标签
|
114 | -s 是否为 socket 数据
|
115 | -m, --method method of mock http
|
116 |
|
117 | init 快速开始
|
118 |
|
119 | -c, --config config file of mock
|
120 | -h, --host host of mock
|
121 | -p, --port port of mock http
|
122 | -d, --directory directory of mock http
|
123 |
|
124 | -v, --version 显示版本号
|
125 | --help 显示帮助信息
|
126 |
|
127 | 示例:
|
128 | fmock init
|
129 | fmock
|
130 | ```
|
131 |
|
132 | #### 启动 mock 服务
|
133 |
|
134 | ```bash
|
135 | $ fmock -c ./mock/config
|
136 | ```
|
137 |
|
138 | #### 新建文件
|
139 |
|
140 | 默认创建 http 服务 data 文件
|
141 | 当添加 `-s` 指令时创建 socket 服务 data 文件
|
142 |
|
143 | ```bash
|
144 | $ fmock create ./aaa/bbb -m '方法名'
|
145 | ```
|
146 |
|
147 | ## config
|
148 |
|
149 | #### 新建
|
150 |
|
151 | 添加 `config.js` 文件
|
152 |
|
153 | #### 参数
|
154 |
|
155 | <table>
|
156 | <tr>
|
157 | <td colspan="3">参数</td>
|
158 | <td>解释</td>
|
159 | <td>数据类型</td>
|
160 | <td>示例</td>
|
161 | </tr>
|
162 | <tr>
|
163 | <td colspan="3">host</td>
|
164 | <td>string</td>
|
165 | <td>ip</td>
|
166 | <td>‘0.0.0.0’</td>
|
167 | </tr>
|
168 | <tr>
|
169 | <td rowspan="7">http</td>
|
170 | <td colspan="2">port</td>
|
171 | <td>number</td>
|
172 | <td>http 服务端口</td>
|
173 | <td>3839</td>
|
174 | </tr>
|
175 | <tr>
|
176 | <td colspan="2">directory</td>
|
177 | <td>string</td>
|
178 | <td>http 数据文件路径</td>
|
179 | <td>‘./data’</td>
|
180 | </tr>
|
181 | <tr>
|
182 | <td colspan="2">middleware</td>
|
183 | <td>array</td>
|
184 | <td>自定义中间件</td>
|
185 | <td>[]</td>
|
186 | </tr>
|
187 | <tr>
|
188 | <td colspan="2">rule</td>
|
189 | <td>string</td>
|
190 | <td>自定义规则</td>
|
191 | <td>‘./rule.js’</td>
|
192 | </tr>
|
193 | <tr>
|
194 | <td rowspan="3">tls</td>
|
195 | <td>httpsPort</td>
|
196 | <td>number</td>
|
197 | <td>https 端口</td>
|
198 | <td>443</td>
|
199 | </tr>
|
200 | <tr>
|
201 | <td>key</td>
|
202 | <td>string</td>
|
203 | <td>密钥</td>
|
204 | <td></td>
|
205 | </tr>
|
206 | <tr>
|
207 | <td>cert</td>
|
208 | <td>string</td>
|
209 | <td>证书</td>
|
210 | <td></td>
|
211 | </tr>
|
212 | <tr>
|
213 | <td rowspan="2">socket</td>
|
214 | <td colspan="2">port</td>
|
215 | <td>number</td>
|
216 | <td>socket 服务端口</td>
|
217 | <td>3838</td>
|
218 | </tr>
|
219 | <tr>
|
220 | <td colspan="2">directory</td>
|
221 | <td>string</td>
|
222 | <td>socket 自定义回复文件</td>
|
223 | <td>‘./socket’</td>
|
224 | </tr>
|
225 | <tr>
|
226 | <td colspan="3">label</td>
|
227 | <td>string</td>
|
228 | <td>自定义 label</td>
|
229 | <td>‘xindongdong’</td>
|
230 | </tr>
|
231 | </table>
|
232 |
|
233 | #### 示例
|
234 |
|
235 | ```javascript
|
236 | module.exports = {
|
237 | host: "0.0.0.0",
|
238 | http: {
|
239 | port: 8888,
|
240 | directory: "./data",
|
241 | middleware: ["a"],
|
242 | rule: "./rule.js",
|
243 | tls: {
|
244 | httpsPort: 443,
|
245 | key: "aaa",
|
246 | cert: "111"
|
247 | }
|
248 | },
|
249 | socket: {
|
250 | port: 3838,
|
251 | directory: "./socket"
|
252 | },
|
253 | label: "chenle"
|
254 | };
|
255 | ```
|
256 |
|
257 | ## data
|
258 |
|
259 | #### 新建
|
260 |
|
261 | 添加 `data` 文件夹,按照以下规则新建文件
|
262 |
|
263 | #### 规则
|
264 |
|
265 | - 一个接口一个文件
|
266 | - 文件路径即路由和方法
|
267 |
|
268 | #### 示例
|
269 |
|
270 | `路由`:/ambassador/courses/:id/:pay
|
271 | `方法`:GET
|
272 |
|
273 | ```tree
|
274 | mock
|
275 | ├── data
|
276 | │ └── ambassador
|
277 | │ ├── courses
|
278 | │ │ └── :id
|
279 | │ │ └── :pay#get.js
|
280 | ```
|
281 |
|
282 | #### 数据定义
|
283 |
|
284 | **JSON 数据**
|
285 |
|
286 | 我们将会直接获取到定义 data 数据。
|
287 |
|
288 | **示例**
|
289 |
|
290 | ```javascript
|
291 | exports.data = {
|
292 | errcode: 0,
|
293 | errmsg: "ok",
|
294 | data: {
|
295 | a: "@number",
|
296 | b: "2",
|
297 | c: [1, 2, 3],
|
298 | d: "@string",
|
299 | type: {
|
300 | __rule__: "@enum", // 枚举
|
301 | params: ["数组", "b", "c"]
|
302 | }
|
303 | }
|
304 | };
|
305 | ```
|
306 |
|
307 | **function**
|
308 |
|
309 | 在 function 中,你可以构造你想要的数据、状态等任何你需要的结构。
|
310 |
|
311 | **示例**
|
312 |
|
313 | ```javascript
|
314 | exports.data = async (ctx, next) => {
|
315 | ctx.status = 200;
|
316 | ctx.body = {
|
317 | aaa: 123,
|
318 | bbb: 234
|
319 | };
|
320 | await next();
|
321 | };
|
322 | ```
|
323 |
|
324 | ## 内部 rule
|
325 |
|
326 | 在构造数据时,我们提供部分内部方法供你快速定义数据
|
327 |
|
328 | #### 简单方法
|
329 |
|
330 | **内置列表**
|
331 |
|
332 | | 参数 | 解释 | 返回数据类型 | 示例 |
|
333 | | :----------: | :--------: | :----------: | :----------------------------------: |
|
334 | | @id | id(8 位) | string | 64669287 |
|
335 | | @name | 姓名 | string | 李健龙 |
|
336 | | @province | 省份 | string | 湖北省 |
|
337 | | @phoneNumber | 电话号码 | string | 13888888888 |
|
338 | | @number | 1-100 整数 | number | 2 |
|
339 | | @price | 价格 | number | 388.00 |
|
340 | | @weekday | 星期 | string | Monday |
|
341 | | @month | 月份 | string | January |
|
342 | | @recentDate | 最近时间 | date | 2019-08-06T06:46:32.360Z |
|
343 | | @futureDate | 未来时间 | date | |
|
344 | | @pastDate | 过去时间 | date | |
|
345 | | @avatar | 头像 | string | |
|
346 | | @animals | 动物图片 | string | |
|
347 | | @sentences | 段落 | string | |
|
348 | | @lines | 一行文本 | string | |
|
349 | | @boolean | 布尔值 | boolean | true |
|
350 | | @uuid | uuid | string | 5c9b344b-a589-4efd-9c95-17508c0c0087 |
|
351 |
|
352 | **示例**
|
353 |
|
354 | ```javascript
|
355 | exports.rules = {
|
356 | books: {
|
357 | a: "@number",
|
358 | b: "2",
|
359 | c: [1, 2, 3],
|
360 | d: "@string"
|
361 | }
|
362 | };
|
363 | ```
|
364 |
|
365 | #### 复杂方法
|
366 |
|
367 | 复杂方法中,需要用参数进行配置,参数说明如下:
|
368 |
|
369 | | 参数 | 解释 | 是否必填 | 数据类型 | 示例 |
|
370 | | :----------: | :--: | :------: | :------: | :-----: |
|
371 | | \_\_rule\_\_ | 标识 | 是 | string | '@list' |
|
372 | | params | 参数 | 是 | object | |
|
373 |
|
374 | **内置列表**
|
375 |
|
376 | | 参数 | 解释 | 返回数据类型 | 示例 |
|
377 | | :---: | :-----------: | :-----------: | :--: |
|
378 | | @enum | 枚举 | string/number | |
|
379 | | @list | 数组/数组对象 | array | |
|
380 |
|
381 | **列表详情**
|
382 |
|
383 | `list`:列表方法,返回一定长度的列表,params 中定义以下参数
|
384 |
|
385 | | 参数 | 解释 | 是否必填 | 数据类型 | 示例 |
|
386 | | :-----: | :----: | :------: | :------: | :--: |
|
387 | | number | 数量 | 否 | number | 3 |
|
388 | | payload | 子数据 | 是 | 任意类型 | |
|
389 |
|
390 | 示例
|
391 |
|
392 | ```javascript
|
393 | exports.rules = {
|
394 | classrooms: {
|
395 | __rule__: "@list",
|
396 | params: {
|
397 | number: 3,
|
398 | payload: {
|
399 | name: "@name"
|
400 | }
|
401 | }
|
402 | }
|
403 | };
|
404 | ```
|
405 |
|
406 | `enum`:枚举方法,返回定义的枚举之一,params 定义如下
|
407 |
|
408 | | 参数 | 解释 | 是否必填 | 数据类型 | 示例 |
|
409 | | :----: | :--: | :------: | :------: | :--: |
|
410 | | params | 参数 | 是 | array | |
|
411 |
|
412 | 示例
|
413 |
|
414 | ```javascript
|
415 | exports.rules = {
|
416 | types: {
|
417 | __rule__: "@enum",
|
418 | params: [1, "@string", 3]
|
419 | }
|
420 | };
|
421 | ```
|
422 |
|
423 | ## 自定义 rule
|
424 |
|
425 | 非必须项,不需要可跳过
|
426 |
|
427 | #### 新建
|
428 |
|
429 | 添加 `rule.js` 文件
|
430 |
|
431 | #### 添加规则
|
432 |
|
433 | - 在 [config](#config) 中添加 `rule` 字段
|
434 | - 通过 key-value 的方式添加规则,其中 key 为规则名,value 为方法
|
435 |
|
436 | **简单规则**
|
437 |
|
438 | 既可以直接返回 number、 string 等**基本类型**,也可以返回 function**高级类型**。
|
439 |
|
440 | 基本类型示例
|
441 |
|
442 | ```javascript
|
443 | module.exports = {
|
444 | houses: {
|
445 | a: 123,
|
446 | b: "@string"
|
447 | }
|
448 | };
|
449 | ```
|
450 |
|
451 | 高级类型示例
|
452 |
|
453 | ```javascript
|
454 | module.exports = {
|
455 | houses: function() {
|
456 | return {
|
457 | 3: 3,
|
458 | 4: 4
|
459 | };
|
460 | }
|
461 | };
|
462 | ```
|
463 |
|
464 | **复杂规则**
|
465 |
|
466 | 当我们需要在方法中传递参数时,我们就需要使用**复杂规则**
|
467 |
|
468 | **示例**
|
469 |
|
470 | ```javascript
|
471 | module.exports = {
|
472 | houses: function(params) {
|
473 | let payload = params.payload;
|
474 | let number = params.number || 1;
|
475 | return {
|
476 | payload,
|
477 | number
|
478 | };
|
479 | }
|
480 | };
|
481 | ```
|
482 |
|
483 | 此时的参数 `params` 便是我们在定义 [data 数据](#数据定义) 时添加的参数
|
484 |
|
485 | **组合使用**
|
486 |
|
487 | 内置规则和自定义规则可以组合使用,进行数据构造
|
488 |
|
489 | **示例**
|
490 |
|
491 | 定义规则:
|
492 |
|
493 | ```javascript
|
494 | module.exports = {
|
495 | houses: function() {
|
496 | return {
|
497 | 3: {
|
498 | __rule__: "@list",
|
499 | params: {
|
500 | payload: {
|
501 | a: "@avatar"
|
502 | }
|
503 | }
|
504 | },
|
505 | 4: 4
|
506 | };
|
507 | }
|
508 | };
|
509 | ```
|
510 |
|
511 | 组合使用:
|
512 |
|
513 | ```javascript
|
514 | exports.data = {
|
515 | errcode: 0,
|
516 | errmsg: "",
|
517 | data: {
|
518 | a: "@houses",
|
519 | b: "2",
|
520 | c: [1, 2, 3],
|
521 | d: "@string",
|
522 | type: {
|
523 | __rule__: "@enum", // 枚举
|
524 | payload: ["数组", "b", "c"]
|
525 | },
|
526 | orders: {
|
527 | name: "cheaa",
|
528 | ids: {
|
529 | __rule__: "@list", // 原子数组
|
530 | number: 10,
|
531 | payload: {
|
532 | id: "@number",
|
533 | name: "@string",
|
534 | rooms: [1, 3],
|
535 | books: {
|
536 | __rule__: "@list",
|
537 | number: 4,
|
538 | payload: {
|
539 | tags: {
|
540 | __rule__: "@enum",
|
541 | payload: [1, 2, 3]
|
542 | }
|
543 | }
|
544 | }
|
545 | }
|
546 | }
|
547 | }
|
548 | }
|
549 | };
|
550 | ```
|
551 |
|
552 | **接口作用域规则**
|
553 |
|
554 | 你可以在定义 [data 数据](#data) 时添加当前接口作用域下的自定义规则
|
555 |
|
556 | ```javascript
|
557 | exports.rules = {
|
558 | a: function() {
|
559 | return 1;
|
560 | }
|
561 | };
|
562 |
|
563 | exports.data = {
|
564 | data: {
|
565 | name: "forchange",
|
566 | desc: "@a"
|
567 | }
|
568 | };
|
569 | ```
|
570 |
|
571 | ## label
|
572 |
|
573 | 通过添加 label 标识,处理数据间的冲突问题
|
574 | `label` 字段对 http 服务和 socket 服务均支持
|
575 |
|
576 | #### 新建
|
577 |
|
578 | 在 [config](#config) 中添加 `label` 字段
|
579 | 新建文档时添加 `-l` 指令指定 `label` 或者手动添加 `&<你的 label 名>`
|
580 |
|
581 | #### 示例
|
582 |
|
583 | 新建
|
584 |
|
585 | ```
|
586 | $ fmock create './aaa/bbb' -l 'xindongdong'
|
587 | ```
|
588 |
|
589 | 你将会生成
|
590 |
|
591 | ```tree
|
592 | mock
|
593 | ├── data
|
594 | │ └── aaa
|
595 | │ ├── bbb#get&xindongdong.js
|
596 | ```
|
597 |
|
598 | ## flow
|
599 |
|
600 | 特殊情况下,可能需要在多个接口之间进行数据的联动处理,你可以在定义 `data` 数据时添加 `flow` 字段
|
601 |
|
602 | #### 规定
|
603 |
|
604 | - 当上一次请求和当前请求中 `flow` 定义的 `method`、`route` 匹配时,我们会返回 `flow` 中定义的 `data 数据`。
|
605 | - 如果当前 `flow` 字段中没有 `data` 字段我们将会强制返回本次请求的 `data` 数据,并抛出警告。
|
606 |
|
607 | #### 结构
|
608 |
|
609 | []{method, route, data}
|
610 |
|
611 | | 参数 | 解释 | 是否必填 | 数据类型 |
|
612 | | :----: | :------------: | :------: | :------: |
|
613 | | method | 方法 | 是 | string |
|
614 | | route | 路由 | 是 | string |
|
615 | | data | 强制返回的数据 | 是 | object |
|
616 |
|
617 | #### 示例
|
618 |
|
619 | ```javascript
|
620 | exports.flow = [
|
621 | {
|
622 | method: "get",
|
623 | route: "/ambassador/courses",
|
624 | data: {
|
625 | a: "/ambassador/courses",
|
626 | b: 234
|
627 | }
|
628 | },
|
629 | {
|
630 | method: "get",
|
631 | route: "/ambassador/settlements",
|
632 | data: {
|
633 | a: "/ambassador/settlements",
|
634 | b: 234
|
635 | }
|
636 | }
|
637 | ];
|
638 | ```
|
639 |
|
640 | ## middleware
|
641 |
|
642 | #### 新建
|
643 |
|
644 | 如果你需要自定义 middleware,你需要在 [config](#config) 中添加 `middleware` 字段
|
645 |
|
646 | #### 示例
|
647 |
|
648 | ```
|
649 | middleware: [
|
650 | async (ctx, next) => {
|
651 | console.log('中间件运行 🐸🐸')
|
652 | await next()
|
653 | },
|
654 | async (ctx, next) => {
|
655 | console.log('中间件运行 🐸🐸')
|
656 | await next()
|
657 | }
|
658 | ]
|
659 | ```
|
660 |
|
661 | ## socket
|
662 |
|
663 | #### 启动 socket 服务
|
664 |
|
665 | 你可以在 [config](#config) 中添加 `socket` 字段开启 socket 服务
|
666 |
|
667 | #### 示例
|
668 |
|
669 | ```
|
670 | socket: {
|
671 | port: 端口号
|
672 | }
|
673 | ```
|
674 |
|
675 | #### 连接
|
676 |
|
677 | 当 socket 服务启动后,我们可以通过 `ws://host: 端口号/任意路由` 进行连接
|
678 |
|
679 | #### 管理
|
680 |
|
681 | 当我们开启 socket 服务后,我们能通过 `host: 端口号/` 查看到所有 socket 连接情况
|
682 | 你可以在此页面上进行连接、断开、发送消息等一系列操作,如图
|
683 |
|
684 | <img src="https://git.forchange.cn/newbility/fe-doc/uploads/b3f833df1ff20477393eb211b7862fac/image.png" width="500px" height= "200px"/>
|
685 |
|
686 | #### 添加自定义回复
|
687 |
|
688 | 你可以在 [config](#config) `socket` 字段中添加 `directory` 字段处理 socket 自定义回复文件
|
689 |
|
690 | 添加 `socket` 文件夹
|
691 |
|
692 | **我们规定:**
|
693 |
|
694 | - 文件路径即是你的路由(连接),如 `/aaa` 连接下对应的文件名为 `aaa.js`
|
695 | - 一个文件对应一种回应方式
|
696 |
|
697 | **示例**
|
698 |
|
699 | ```javascript
|
700 | module.exports = async message => {
|
701 | return {
|
702 | name: "forchange",
|
703 | message: message
|
704 | };
|
705 | };
|
706 | ```
|
707 |
|
708 | #### 添加 label
|
709 |
|
710 | 参考 [label](#label)
|