Jest测试时clearV1未正确清空dataStore数据问题排查
1. 数据存储文件(dataStore)实现
let data = { users: [], channels: [], }; // Use get() to access the data function getData() { return data; } // Use set(newData) to pass in the entire data object, with modifications made function setData(newData) { data = newData; } export { getData, setData };
2. 清空函数clearV1实现
import { getData, setData } from './dataStore'; function clearV1() { let data = { users: [], channels: [], }; setData(data); return {}; } export { clearV1 };
在Jest测试环境中运行代码时,调用clearV1()函数无法按预期清空dataStore中的数据。已知authRegisterV1函数用于创建用户并写入数据存储,channelCreateV1函数用于创建频道并写入数据存储。
按以下顺序执行逻辑:
- 调用
authRegisterV1('test1@gmail.com','test123','Firt','Last')创建第一个用户 - 调用
clearV1()尝试清空全量数据 - 调用
authRegisterV1('test2@gmail.com','test123','Firt','Last')创建第二个用户
预期执行结果为:数据存储的users数组仅包含第二个注册用户(uId为1,邮箱为test2@gmail.com),channels数组为空;但实际运行结果为users数组同时包含第一次、第二次注册的两个用户,channels数组为空。
已确认clearV1函数本身的实现逻辑正确,且已在测试文件中导入所有依赖的函数,以下是导致该异常的可能原因:
模块路径解析不一致导致多实例问题
Jest的模块缓存以解析后的绝对文件路径为唯一键。如果clearV1.js、authRegisterV1.js、测试文件三者导入dataStore的路径写法不统一(比如混用相对路径和路径别名、相对路径层级书写错误、大小写不匹配),Jest会将其识别为多个不同的模块,分别加载独立的副本,每个副本维护自己私有的data变量。此时clearV1()只会修改自己引用的那份dataStore副本的数据,authRegisterV1操作的是另一份完全独立的dataStore实例,清空操作自然不会生效。业务函数未遵循dataStore读写规范
authRegisterV1等业务函数没有按照要求每次操作时调用getData()获取最新数据、修改后调用setData()提交更新,而是在模块加载阶段就缓存了data对象的引用(比如在模块顶层写const dataCache = getData(),后续所有增删改操作都直接修改这个dataCache),甚至在函数闭包内维护独立的users/channels数组,完全不依赖dataStore的读写。这种情况下clearV1()虽然替换了dataStore内部的data变量指向的新对象,但业务函数手里还握着旧的data对象引用,后续新增用户会直接写入旧对象,最终读取数据时就会看到两次注册的用户同时存在。Jest配置或测试用例写法问题
如果测试中错误使用了jest.resetModules()、jest.isolateModules()等模块重置API,但重置后没有重新导入所有依赖的业务函数,会导致部分函数持有的是模块重置前的旧引用,操作的是旧的dataStore实例。另外如果测试文件中手动mock过dataStore模块,且mock实现的setData/getData逻辑不符合预期(比如没有正确替换内部存储的对象),也会导致清空操作失效。存在独立于dataStore的状态存储
如果用户uId的自增计数器、用户/频道的缓存映射没有存在dataStore的data对象内,而是定义在dataStore外部的模块级变量、或者其他业务模块的闭包中,clearV1()重置dataStore时不会同步清空这部分状态,也会导致数据异常。
内容的提问来源于stack exchange,提问作者FreeAntiVirus

