装饰公司网站如何布局,代做毕业设计网站有哪些,房产网站案例,国外10条新闻简短React 测试笔记 03 - 测试 Redux 中 Reducer 状态变化
这段时间都在重构代码#xff0c;把本来奇奇怪怪(singleton)的实现改成用 redux 的实现#xff0c;然后就突然想到……即然 redux 的改变不涉及到 UI 的改变#xff0c;那么是不是说可以单独写 redux 的测试……#…React 测试笔记 03 - 测试 Redux 中 Reducer 状态变化
这段时间都在重构代码把本来奇奇怪怪(singleton)的实现改成用 redux 的实现然后就突然想到……即然 redux 的改变不涉及到 UI 的改变那么是不是说可以单独写 redux 的测试……
找了一下资料发现比想象中的简单很多所以就稍微记一下
要求简述
这里的案例主要就是通过用户权限然后 map 对应的页面的读写(全套为增删改查)权限基本逻辑是这样的 权限分为 read_only、full、A、B、C(A/B/C 为抽象的权限基本上说对应一下用户可以获得不同分类的权限) 每个权限对应着每个页面的读写操作 如 full 代表着用户对所有页面都有读写操作read_only 相反 A/B/C 给予用户 页面 A/页面 B/页面 C 的读写权限 用户可以有不同的权限 更高的权限将会复写较低的权限 理论上来说这是不可能发生的事情不过假设用户同时有 read_only 和 full那么 full 将会复写 read_only 的权限 比较通常发生的是用户可能会有 A/B/C 这样的权限但是其中对某些页面会有覆盖操作如权限 B 给予 B 页面读写操作权限 C 可能对 B 页面有改的权限但是没有增删 当前 redux 状态只负责授予权限具体权限的处理则是在 路由/component 处进行处理
实现简述
具体的实现就是 dispatch event 实现权限的 map 则是通过 {page: permission} 的方式进行存储至于要求说是取最高权限因此 permission 的实现方式采用数字在 redux 中使用 Math.max() 的方式取最大值
需要注意的是这里在实现的时候可能会出现 type casting 的异常如 0 | 1 | 2 | 3 is not compatible with number 之类的我的实现方式是用 Math.max() as (typeof T)[keyof typeof T] 的方式进行一个转型
当然cv 太多的话还是建议抽一个函数取实现
这样的话通过 roles.filter(role role in SOME_CONST) 中可以获取需要 map 的权限再遍历数组更新权限即可
测试实现
首先我先 mock 了一下整个 app component
jest.mock(../../App, () ({}));这一步是假设 app component 已经渲染完毕了因为其他的 redux 对一些 util——有可能是异步的操作——有一些依赖的关系然后目前 jest 没有找到这些 util因此就会抛出找不到 util 的异常所以这里先 mock 一下虚构的 app让 jest 知道内部的 util 实现不重要最终结果就是所有的组件已经正常渲染完毕
其主要原因也是我们的项目有一些 context wrapper 了
// redux
Provider store{store}{/* async code从 API 处获得权限再传到 redux 中 */}{/* 如果不 mock从这里就会报错 */}AccessContextOtherContextApp //OtherContext/AccessContext
/Provider之后的操作非常的简单jest 已经默认 app 可以正确渲染因此这里只需要出发 RTK 的状态变化并且检查状态变化即可如
import {ISliceType,sliceReducer,exportedAction,
} from ../../store/slices/state/keycloak;jest.mock(../../App, () ({}));// 这里可以实现一些状态用来方便 test而不用手写一堆代码
const initialState: ISliceType {}; // AKA readOnly
const fullAccess: ISliceType {};
const aAccess: PartialISliceType {};
const bAccess: PartialISliceType {};const validationHelper (grantedPerm: ISliceType,checkAccess: PartialISliceType
): boolean {// do some check// 也许可以遍历 checkAccess 的 key保证 grantedPerm[key] checkAccess[key] 这种return true;
};describe(test user has readOnly access, () {test(initial state is set to readOnly, () {expect(sliceReducer(undefined, { type: undefined })).toEqual(initialState);});test(user only has readOnly access, () {const payload { role: [readonly] };expect(sliceReducer(initialState, exportedAction(payload))).toEqual(initialState);});test(user has multiple role, () {const payload { role: [readonly, aAccess] };const grantedPerm sliceReducer(initialState,exportedAction(payload)).permission;expect(validationHelper(grantedPerm, initialState)).toBeTruthy();expect(validationHelper(grantedPerm, aAccess)).toBeTruthy();expect(validationHelper(grantedPerm, bAccess)).toBeFalsy();});
});这里检查了三种状态 undefined 相当于触发 Reducer 的初始化因此返回初始状态 因为初始状态直接声明了所以可以直接用 toEqual 去测试 测试只读状态 这里假设返回的状态依旧比较简单因此仍旧可以使用 toEqual 去和整个状态测试 测试更复杂的状态 这个就是用的第三个方法主要是用 toBeTruthy() 和 toBeFalsy() 测试 假设说页面有十几二十个然后有四五组权限全都手写的话会引入更多的 human error这个时候就可以考虑将一些常量抽出来 比如说 a 权限对应的 {page: access} 可以单独抽出来做一个变量——这个部分是可以实现、测试公用的然后再写一个 helper function——这里可以用于测试给予的权限是否大于等于变量中的权限而没有赋值的权限是否小于等于当前权限这个根据具体需求具体实现 我这里的 helper 直接返回了 boolean使用上面列举的两个方式去测试不过实际上还可以搭配其他的 Modifiers 和 Matchers 去测试不仅仅是用 truthy/falsy 这个不是必须的因为测试有些情况下就是会 cv 一些代码而且一些复杂的情况下仅返回 boolean 的 helper 也许不是这么的好用那么最差情况下就是得一遍遍手写然后通过 npm test 去运行测试结果
reference Writing Tests 这里只用了单元测试 reducer 的部分 Expect 下面的 reference 列举了 Modifiers 和 Matchers