Christsam 2019-06-25
// 无论什么方法,都是这样一个结构 const fn = () => { };
比如,我要写一个接口,查询组织下的设备列表 /api/device/list
const deviceList = (params) => { // 传入一些参数 return []; // 返回一个列表 };
我需要哪些参数:
输出结果很简单,为一个数组。
第一步分析,存在成功和错误(错误类型先不考虑)两种类型的结果。
// 成功 // 错误 const deviceList = async (ctx) => { // 错误 if(someError) { // 返回错误结果 } // 成功 return getDevicesByOid(oid); };
这是一个大概的设想,没有必要将代码写出来。然后润化该思路,写出第一段框架。
首先,传入的参数为组织 oid,用户的信息可以通过 session(或其他方式)从内部获得。
// 成功 // 错误 // 错误1:用户未加入组织 // 错误2:传入参数组织不存在 // 错误3:用户无组织权限 // 传入参数: 要查询的组织 oid // 能够通过 session 取到的信息: user const deviceList = async (ctx) => { // 用户信息 ctx.user // 判断用户是否有组织 if (ctx.user.oid === 0) { // 错误1:用户未加入组织 } // 如果不传该参数,查询当前用户组织的设备 const { oid = ctx.user.oid } = ctx.request.body; if (oid === ctx.user.oid) { // 成功 return getDevicesByOid(oid); } // 根据oid查询组织信息 // 错误2:传入参数组织不存在 // 判断是否有权限 const checkRights = await checkUserOrgRights(ctx.user.uid, oid); if (!checkRights) { // 错误3:用户无组织权限 } // 成功 return getDevicesByOid(oid); };
// 成功 // 错误 // 错误1:用户未加入组织 // 错误2:传入参数组织不存在 // 错误3:用户无组织权限 // 传入参数: 要查询的组织 oid // 能够通过 session 取到的信息: user const deviceList = async (ctx) => { // 用户信息 ctx.user // 判断用户是否有组织 if (ctx.user.oid === 0) { // 错误1:用户未加入组织 } // 如果不传该参数,查询当前用户组织的设备 const { oid = ctx.user.oid } = ctx.request.body; if (oid !== ctx.user.oid) { // 为什么这里不用等于判断:如果等于的话,则当时就需要返回出去,这样的话该方法会有两个成功的 return // 根据oid查询组织信息 // 错误2:传入参数组织不存在 // 判断是否有权限 const checkRights = await checkUserOrgRights(ctx.user.uid, oid); if (!checkRights) { // 错误3:用户无组织权限 } } // 成功 return getDevicesByOid(oid); };
完成其他的业务代码。
按照上面推荐方式完成代码后,需要进行代码的测试。
首先需要明确业务的流程,理清测试的思路。
错误
主要有两种设计思路:
这是传统的单元测试衍生而来的 BDD 测试方式。
这里测试用例的个数应该为8
次:
成功:
其中,测试3-5可以优化为一次测试(即根据所有管理员 uid 的数组比较是否包含当前用户 uid),最终优化后的结果应当为6
次。
但由于该思路中不明确用户,所以用户行为无法准确表达,在创建测试数据的时候较为困难,不仔细思考分析,无法优化需要创建多少条测试数据。
而实际上 BDD 测试为用户行为测试,可以以几类用户的情形分别进行测试。
以此循环,直至覆盖所有。
用户1(非组织管理员,查询自己的组织)
用户2(上级某组织管理员)(组织3)
用户3(未加入组织用户)
非常简洁明了的关系,需要3个测试用户,3个组织(上下级关系进行数据复用,一个无权限的组织),即可涵盖所有范围。
最终优化版设计:
用户1(某组织管理员,有下级组织)
用户2(未加入组织用户)
两个用户,三个组织。完成所有覆盖。
可以从上述测试思路二中进行反推。
实际上思路可能是在写代码或者写测试的过程中不断的改进和完善的。