|
@@ -0,0 +1,192 @@
|
|
|
+# 像盖房子一样写代码
|
|
|
+
|
|
|
+## 当我写一个功能模块方法时,我在想些什么
|
|
|
+
|
|
|
+```js
|
|
|
+// 无论什么方法,都是这样一个结构
|
|
|
+const fn = () => {
|
|
|
+
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+比如,我要写一个接口,查询组织下的设备列表 `/api/device/list`
|
|
|
+
|
|
|
+### 地基
|
|
|
+
|
|
|
+```js
|
|
|
+const deviceList = (params) => { // 传入一些参数
|
|
|
+ return []; // 返回一个列表
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+我需要哪些参数:
|
|
|
+
|
|
|
+- 用户基本信息(主要是用户 id,用户的组织 id)
|
|
|
+- 用户对应的组织基本信息(主要是组织 id,组织管理员 id,层级关系,以及权限逻辑)
|
|
|
+
|
|
|
+输出结果很简单,为一个数组。
|
|
|
+
|
|
|
+### 浇筑
|
|
|
+
|
|
|
+第一步分析,存在成功和错误(错误类型先不考虑)两种类型的结果。
|
|
|
+
|
|
|
+```js
|
|
|
+// 成功
|
|
|
+// 错误
|
|
|
+const deviceList = async (ctx) => {
|
|
|
+ // 错误
|
|
|
+ if(someError) {
|
|
|
+ // 返回错误结果
|
|
|
+ }
|
|
|
+ // 成功
|
|
|
+ return getDevicesByOid(oid);
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+这是一个大概的设想,没有必要将代码写出来。然后润化该思路,写出第一段框架。
|
|
|
+
|
|
|
+### 主体结构
|
|
|
+
|
|
|
+首先,传入的参数为组织 oid,用户的信息可以通过 session(或其他方式)从内部获得。
|
|
|
+
|
|
|
+#### 可能的一种思路
|
|
|
+
|
|
|
+```js
|
|
|
+// 成功
|
|
|
+// 错误
|
|
|
+// 错误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);
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+#### 推荐的实现方式
|
|
|
+
|
|
|
+```js
|
|
|
+// 成功
|
|
|
+// 错误
|
|
|
+// 错误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);
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+### 封顶
|
|
|
+
|
|
|
+完成其他的业务代码。
|
|
|
+
|
|
|
+## 当我写一段测试的时候,我在想什么
|
|
|
+
|
|
|
+按照上面推荐方式完成代码后,需要进行代码的测试。
|
|
|
+
|
|
|
+首先需要明确业务的流程,理清测试的思路。
|
|
|
+
|
|
|
+- 成功
|
|
|
+- 错误
|
|
|
+ - 错误1:用户未加入组织
|
|
|
+ - 错误2:传入参数组织不存在
|
|
|
+ - 错误3:用户无组织权限
|
|
|
+
|
|
|
+主要有两种设计思路:
|
|
|
+
|
|
|
+### 设计思路
|
|
|
+
|
|
|
+#### 思路一
|
|
|
+
|
|
|
+1. 完成测试用例,覆盖成功的所有情况
|
|
|
+2. 完成测试用例,覆盖错误1的所有情况
|
|
|
+3. 完成测试用例,覆盖错误2的所有情况
|
|
|
+4. 完成测试用例,覆盖错误3的所有情况
|
|
|
+
|
|
|
+这是传统的单元测试衍生而来的 BDD 测试方式。
|
|
|
+
|
|
|
+这里测试用例的个数应该为`8`次:
|
|
|
+
|
|
|
+- 成功:
|
|
|
+ - 1.当前组织的用户有传入组织 oid
|
|
|
+ - 2.当前组织的用户未传入组织 oid
|
|
|
+ - 3-5.上级组织,上上级组织,根级组织的管理员用户传入组织 oid
|
|
|
+- 6.失败1:用户未加入组织
|
|
|
+- 7.失败2:传入参数组织不存在
|
|
|
+- 8.失败3:用户无组织权限
|
|
|
+
|
|
|
+其中,测试3-5可以优化为一次测试(即根据所有管理员 uid 的数组比较是否包含当前用户 uid),最终优化后的结果应当为`6`次。
|
|
|
+
|
|
|
+但由于该思路中不明确用户,所以用户行为无法准确表达,在创建测试数据的时候较为困难,不仔细思考分析,无法优化需要创建多少条测试数据。
|
|
|
+
|
|
|
+#### 思路二
|
|
|
+
|
|
|
+而实际上 BDD 测试为用户行为测试,可以以几类用户的情形分别进行测试。
|
|
|
+
|
|
|
+1. 模拟一个用户的数据,覆盖成功和可能错误(有可能无法涵盖到所有错误)的所有情况
|
|
|
+2. 根据未覆盖的部分,再模拟另一个用户的数据,覆盖成功和可能错误(有可能无法涵盖到所有错误)的所有情况
|
|
|
+
|
|
|
+以此循环,直至覆盖所有。
|
|
|
+
|
|
|
+- 用户1(非组织管理员,查询自己的组织)
|
|
|
+ - 1.成功(未传入组织 oid)
|
|
|
+ - 2.成功(传入组织 oid)
|
|
|
+ - 3.失败2:传入参数组织不存在
|
|
|
+ - 4.失败3:用户无组织权限
|
|
|
+- 用户2(上级某组织管理员)
|
|
|
+ - 5.成功
|
|
|
+- 用户3(未加入组织用户用户)
|
|
|
+ - 6.失败1:用户未加入组织
|
|
|
+
|
|
|
+非常简洁明了的关系,需要3个测试用户,2个组织(上下级关系进行数据复用),即可涵盖所有范围。
|
|
|
+
|
|
|
+## 当我以测试驱动开发的时候,我在想些什么
|
|
|
+
|
|
|
+可以从上述测试思路二中进行反推。
|
|
|
+
|
|
|
+
|