当前位置: 首页 > news >正文

React 测试笔记 03 - 测试 Redux 中 Reducer 状态变化

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,从这里就会报错 */}<AccessContext><OtherContext><App /></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: Partial<ISliceType> = {};
const bAccess: Partial<ISliceType> = {};const validationHelper = (grantedPerm: ISliceType,checkAccess: Partial<ISliceType>
): 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

相关文章:

React 测试笔记 03 - 测试 Redux 中 Reducer 状态变化

React 测试笔记 03 - 测试 Redux 中 Reducer 状态变化 这段时间都在重构代码&#xff0c;把本来奇奇怪怪(singleton)的实现改成用 redux 的实现&#xff0c;然后就突然想到……即然 redux 的改变不涉及到 UI 的改变&#xff0c;那么是不是说可以单独写 redux 的测试……&#…...

xilinx primitives(原语)

Xilinx的原语分为10类&#xff0c;包括&#xff1a;计算组件&#xff0c;IO端口组件&#xff0c;寄存器/锁存器&#xff0c;时钟组件&#xff0c;处理器组件&#xff0c;移位寄存器&#xff0c;配置和检测组件&#xff0c;RAM/ROM组件&#xff0c;Slice/CLB组件&#xff0c;G-t…...

机器学习 - DBSCAN聚类算法:技术与实战全解析

目录 一、简介DBSCAN算法的定义和背景聚类的重要性和应用领域DBSCAN与其他聚类算法的比较 二、理论基础密度的概念核心点、边界点和噪声点DBSCAN算法流程邻域的查询聚类的形成过程 参数选择的影响 三、算法参数eps&#xff08;邻域半径&#xff09;举例说明&#xff1a;如何选择…...

kafka微服务学习

消息中间件对比&#xff1a; 1、吞吐、可靠性、性能 Kafka安装 Kafka对于zookeeper是强依赖&#xff0c;保存kafka相关的节点数据&#xff0c;所以安装Kafka之前必须先安装zookeeper Docker安装zookeeper 下载镜像&#xff1a; docker pull zookeeper:3.4.14创建容器 do…...

5G网络切片,到底是什么?

网络切片&#xff0c;是5G引入的一个全新概念。 一看到切片&#xff0c;首先想到的&#xff0c;必然是把一个完整的东西切成薄片。于是&#xff0c;切面包或者切西瓜这样的画面&#xff0c;映入脑海。 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 然而…...

linux安装nodejs

写在前面 因为工作需要&#xff0c;需要使用到nodejs&#xff0c;所以这里简单记录下学习过程。 1&#xff1a;安装 wget https://nodejs.org/dist/v14.17.4/node-v14.17.4-linux-x64.tar.xz tar xf node-v14.17.4-linux-x64.tar.xz mkdir /usr/local/lib/node // 这一步骤根…...

第1天:Python基础语法(一)

** 1、Python简介 ** Python是一种高级、通用的编程语言&#xff0c;由Guido van Rossum于1989年创造。它被设计为易于阅读和理解&#xff0c;具有简洁而清晰的语法&#xff0c;使得初学者和专业开发人员都能够轻松上手。 Python拥有丰富的标准库&#xff0c;提供了广泛的功…...

ppt聚光灯效果

1.放入三张图片内容或其他 2.全选复制成图片 3.设置黑色矩形&#xff0c;透明度30% 4.粘贴复制后的图片&#xff0c;制定图层 5.插入椭圆&#xff0c;先选中矩形&#xff0c;再选中椭圆&#xff0c;点击绘图工具&#xff0c;选择相交即可&#xff08;关键&#xff09;...

图文解析 Nacos 配置中心的实现

目录 一、什么是 Nacos 二、配置中心的架构 三、Nacos 使用示例 &#xff08;一&#xff09;官方代码示例 &#xff08;二&#xff09;Properties 解读 &#xff08;三&#xff09;配置项的层级设计 &#xff08;四&#xff09;获取配置 &#xff08;五&#xff09;注册…...

P1918 保龄球

Portal. 记录每一个瓶子数对应的位置即可。 注意到值域很大&#xff08; a i ≤ 1 0 9 a_i\leq 10^9 ai​≤109&#xff09;&#xff0c;要用 map 存储。 #include <bits/stdc.h> using namespace std;map<int,int> p;int main() {int n;cin>>n;for(int i…...

SAP-PP-报错:工作中心 7333_JQ 工厂 7331 对任务清单类型 N 不存在

创建工艺路线时报错&#xff1a;工作中心 7333_JQ 工厂 7331 对任务清单类型 N 不存在&#xff0c; 这是因为在创建工作中心时未维护控制键值导致的...

MySQL -- 用户管理

MySQL – 用户管理 文章目录 MySQL -- 用户管理一、用户1.用户信息2.创建用户3.删除用户4.远端登录MySQL5.修改用户密码6.数据库的权限 一、用户 1.用户信息 MySQL中的用户&#xff0c;都存储在系统数据库mysql的user表中&#xff1a; host&#xff1a; 表示这个用户可以从…...

IOS浏览器不支持对element ui table的宽度设置百分比

IOS浏览器不支持对element ui table的宽度设置百分比 IOS浏览器会把百分号识别成px&#xff0c;所以我们可以根据屏幕宽度将百分比转换成px getColumnWidth(data) {const screenWidth window.innerWidth;const desiredPercentage data;const widthInPixels (screenWidth *…...

Vue+OpenLayers 创建地图并显示鼠标所在经纬度

1、效果 2、创建地图 本文用的是高德地图 页面 <div class"map" id"map"></div><div id"mouse-position" class"position_coordinate"></div>初始化地图 var gaodeLayer new TileLayer({title: "高德地…...

01-编码-H264编码原理

1.整体概念 编码的含义就是压缩,将摄像头采集的YUV或RGB数据压缩成H264。 压缩的过程就是去除信息冗余的过程,一般视频有如下的冗余信息。 (1)空间冗余:在同一个画面中,相邻的像素点之间的变化很小,因而可以用一个特定大小的矩阵来描述相邻的这些像素。 (2)时间冗余:…...

RxJava/RxAndroid的操作符使用(二)

文章目录 一、创建操作1、基本创建2、快速创建2.1 empty2.2 never2.3 error2.4 from2.5 just 3、定时与延时创建操作3.1 defer3.2 timer3.3 interval3.4 intervalRange3.5 range3.6 repeat 二、过滤操作1、skip/skipLast2、debounce3、distinct——去重4、elementAt——获取指定…...

【C语法学习】20 - 文件访问顺序

文章目录 0 前言1 文件位置指示符2 rewind()函数2.1 函数原型2.2 参数2.3 返回值2.4 使用说明 3 ftell()函数3.1 函数原型3.2 参数3.3 返回值 4 fseek()函数4.1 函数原型4.2 参数4.3 返回值 5 示例5.1 示例15.2 示例2 0 前言 C语言文件访问分为顺序文件访问和随机文件访问。 …...

Etcd 常用命令与备份恢复

1. etcd简介 官方网站&#xff1a;etcd.io 官方文档&#xff1a;etcd.io/docs/v3.5/op-guide/maintenance 官方硬件推荐&#xff1a;etcd.io/docs/v3.5/op-guide/hardware github地址&#xff1a;github.com/etcd-io/etcd etcd是CoreOS团队于2013年6月发起的开源项目&#xf…...

获取任意时间段内周、季度、半年的二级联动

#需求是获取两个时间内 年周 、年季度、年半年的二级联动# 找了半天也找不到什么有用的信息 就自己简单写了一个 思路是先获取年的列表再去嵌套查询 根据前端VUE提供的格式嵌套 public function getDate(){$leixing Request::param(leixing);$larr array(1,2,3,4);if(empty(…...

前端面试系列之工程化篇

如果对前端八股文感兴趣&#xff0c;可以留意公重号&#xff1a;码农补给站&#xff0c;总有你要的干货。 前端工程化 Webpack 概念 本质上&#xff0c;webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时&#xff0c;它会在内部从一个…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...