user/* 编写规范
user configs 代码生成
node generate
命令会批量处理所有的接口文件,根据每个接口的定义生成对应的配置,如
export async function getUserInfo() {
return await post({
url: '/api/user/info',
data: {}
});
}
则生成内容为:
'getUserInfo': {
'__proto__': base.getUserInfo,
'api': apis.getUserInfo,
'desc': "获取用户信息",
'url': '/api/user/info',
'defaults': {
},
'generated': {},
'user': {},
'geneArgs': [
{
'scene': 'default',
'desc': '',
'type': NORMAL,
'func': async (ctx) => {
ctx.generated = {};
}
},
],
},
部分字段意义如下:
- defaults: 配置默认参数
- generated: 接收生成参数的对象,在 geneArgs 中编写生成参数的逻辑
- geneArgs: 参数生成函数数组,数组中每个元素是一个如下结构的对象,具体稍后解释
- 其他参数不要改动
另外支持部分额外的属性配置:
after/before -- after/before request callback
可以配置在 api (top level),或者配置在 gene,其中 info 参数为请求结果:
'getOne': {
'__proto__': base.getOne,
'api': apis.getOne,
'desc': "获取优惠券信息",
'url': '/scmsapi/coupon/getOne',
'defaults': {
id: '',
},
'generated': {},
'user': {},
'after': (ctx, info) => { // <------------------------ api level
// console.debug('top level after', ctx, info);
},
'geneArgs': [
{
'scene': 'default',
'desc': '',
'type': NORMAL,
'func': async (ctx) => {
ctx.generated = {
id: await getOneID(),
};
},
'validate': (ctx, info) => {
},
'after': (ctx, info) => {
ctx.after(ctx, info); // <------------------------ gene level
// console.debug('gene level after', ctx, info);
},
__init 模块异步初始化
有的时候模块加载完成后还需要进行异步初始化操作,我们可以将这些操作放在一个名为 __init
的异步函数中,这样 powder 会等待初始化完成才进行后续操作(比如批量验证)
// powder/user/scms/coupon.js
import * as scms from '../../../tests/scms';
// 公共缓存数据
let scmsCommon;
// 异步初始化接口
export const __init = async(force = false) => {
scmsCommon = await scms.scmsCommon(force);
};
在切换选择的模块后,也会自动调用切换模块的__init
:
- 注意,刷新时会自动加载上次选择的模块,进而调用该模块的
__init
上面的情况都是自动调用,但有一种情况,需要手动调用:有时在 A 中引用了 B,如果 B 需要异步初始化,需要手动调用 B 的 __init,注意 return 了 __init 返回的 promise:
// powder/user/client/shop/coupon.js
import * as scms_coupon from '../../../user/scms/coupon';
// 异步初始化接口
export const __init = async() => {
return scms_coupon.__init();
};
geneArgs 参数生成函数数组配置
默认会生成一个函数,可以配置 0 - n 个,配置样例如下,用法参见注释:
'geneArgs': [
{
'scene': 'default', // 生成函数名
'desc': '默认,生成一个普通订单', // 生成函数描述
'type': NORMAL, // 见 type 节
'func': async (ctx) => {
/*
* 注意这是一个 async 函数,可以通过任何必要方式生成参数
*/
ctx.generated = {
order_no: (await createOrder()).order_no,
};
}
},
]
此函数即为参数生成(回调)函数。
函数默认接收一个 ctx 参数。ctx.generated
为接收生成参数的对象。ctx为当前接口配置对象。
可以为函数增加额外的参数。
'confirm': {
'geneArgs': [
{
'scene': 'default',
'desc': '',
'type': NORMAL,
'func': async (ctx, arg1, arg2) => {
ctx.generated = {};
}
},
],
}
// 则传参调用为(注意第一个参数传递场景值):
confirm.make('default', arg1, arg2);
type
可能值:NORMAL, INNER, GENE_ONLY, NO_BATCH
- INNER 与 GENE_ONLY 配合使用,类似于 NO_BATCH,并且不会在 UI 显示
- 注意,INNER 没有 private 的意思,可以跨越接口调用
- GENE_ONLY 一般与 INNER 配合使用来批量处理数据,因此生成的参数再用来请求没有意义,UI 测试时请求按钮背景会变为黄色以提醒
- 案例:批量确认收货、批量发货
- create-multi-sku-comfirm-all
- create-multi-sku-dispatch-all
- 案例:批量确认收货、批量发货
- NORMAL 默认值
- NO_BATCH 批量时不执行(批量指以下三个按钮触发的批量操作)
noCode
可选。如果设为 true,则不校验返回数据的 code
{
'scene': 'with-no',
'desc': '用于发货的时候指定物流单号调用',
'type': INNER,
'noCode': true,
'func': async (ctx,express_number) => {
ctx.generated = {
express_number
};
}
完整验证
配置示例:
'geneArgs': [
{
'scene': 'default',
'func': async (ctx) => {
ctx.generated = {
// 1
};
},
'validate': (ctx, info) => {
ctx.__info = info; // 2
},
'testSuites': [
{
'name': 'suc',
'validate': (ctx, info) => {
// 5
},
'tests': [
(ctx) => { // 3
ctx.generated.type = '1';
},
(ctx) => {
ctx.generated.code = 1;
return (ctx, info) => {
// 4
}
},
]
},
{
'name': 'fail',
'validate': (ctx, info) => {
utils.assert(info.data.code === 10000, '参数验证失败');
},
'tests': [
(ctx) => {
ctx.generated.type = '0';
},
]
}
],
},
示例解释
对每一个接口,会遍历其所有的参数生成函数
(后面简称gene
),调用一次gene
生成参数,再调用若干次接口本身发送请求,对请求结果进行如下验证(为了描述简洁,下面的 1 2 3 ... 与注释对应):
- 调用
gene
,生成参数- 这里写
gene
的实现代码 1- 注:如果存在接口依赖,需要调用其他接口获取数据,注意用 await 等待接口返回
- 注:
gene
生成的是公共参数,会被所有的后续tests
共享
- 这里写
gene
返回后,所有参数就绪,调用接口本身发送请求,得到返回结果info
- 检验状态码(没有配置 noCode 的情况下)
- 将
info
与 api 配置的返回值进行比对(如果有) - 对
info
进行验证 2
- 获取
testSuites
,对每一个test suite
进行如下操作:- 获取当前 suite 的
tests
(test cases), 每一个test
即为一个测试单元(对应一个函数 3),会做以下事情:- 执行前置操作(
setup
),会在gene
生成的公共参数基础之上修改 3 - 调用接口本身发送请求,得到返回结果
info
- 对
info
进行验证(三级验证,但仅调用优先级最高的一个)- if return false, 不做任何验证
- if
setup
返回一个函数,则此函数将作为验证函数被回调;如果返回值是false
, 则跳过 request & validate 4 - else if 当前
test suite
定义了validate
,则此validate
将作为验证函数被回调 5 - else if 当前
gene
定义了validate
,则此validate
将作为验证函数被回调 2
- 执行前置操作(
- 获取当前 suite 的
更多示例
每一个 test 也可以是异步函数:
'tests': [
async (ctx) => {
ctx.generated.type = '0';
},
tests 也可以配置为一个数组,数组的元素为参数必传字段名,这样会为每个字段发起一次请求,此请求不会包含此字段,用以验证后端是正确否处理字段缺失的情况。
'testSuites': [
{
'name': 'fail',
'validate': assertParamEmpty, // 通用的字段缺失报错验证
'tests': ['aid', 'path', 'version'],
},