如何在Jest中模拟Google Apps Script代码的全局变量
如何在Jest测试中模拟Google Apps Script全局变量subjectCounter?
问题背景
学校使用Google Sheets作为班级手册,通过Google Apps Script(GAS)编写了学生缺勤统计脚本,当前正在用Jest进行单元测试重构,核心问题是:如何模拟updateSubjectCounter函数依赖的全局变量subjectCounter?
相关代码结构如下:
main.js中的全局变量与函数
const absenceFileId = "some_string" const teachersFileId = "some_string" const studentGroupsId = "some_string" const classbookFolderId = "some_string" // STATE let teachers = {} let groups = {} let subjectCounter = {} let errors = [] let students = {} function setupStateVariables() { teachers = getTeachers(removeEmptyRowsAndHeader(getValuesArray(teachersFileId, 0, 1, 1, 50, 3))); groups = getGroups(removeEmptyRowsAndHeader(getValuesArray(studentGroupsId, 0, 1, 1, 400, 6))) subjectCounter = createSubjectCounter(removeEmptyRowsAndHeader(getValuesArray(studentGroupsId, 1, 1, 1, 200, 4))) } function updateSubjectCounter(group, schoolClass, subject) { if (!(subject in subjectCounter[schoolClass])) { return } if (group) { subjectCounter[schoolClass][subject][group] += 1 } else { let keys = Object.keys(subjectCounter[schoolClass][subject]) if (keys.length > 1) { for (let key of keys) { subjectCounter[schoolClass][subject][key] += 1 } } else { subjectCounter[schoolClass][subject][null] += 1 } } } // 测试导出 if (typeof module !== 'undefined') { module.exports = { "getTeachers": getTeachers, "getGroups": getGroups, "createSubjectCounter": createSubjectCounter, "updateSubjectCounter": updateSubjectCounter } }
测试文件main.test.js(当前写法)
const main = require('./main'); test("Test funkce updateSubjectCounter", () => { main.updateSubjectCounter("", "a", "IFM") expect(subjectCounter).toEqual({something}) })
解决方案
以下提供三种可行的解决方式,按推荐程度排序:
1. 重构函数,消除全局依赖(最推荐)
将subjectCounter作为参数传入updateSubjectCounter,让函数成为不依赖全局状态的纯函数,这是最利于测试和代码维护的方案:
修改updateSubjectCounter函数:
function updateSubjectCounter(subjectCounter, group, schoolClass, subject) { if (!(schoolClass in subjectCounter) || !(subject in subjectCounter[schoolClass])) { return; } if (group) { subjectCounter[schoolClass][subject][group] += 1; } else { const keys = Object.keys(subjectCounter[schoolClass][subject]); if (keys.length > 1) { for (const key of keys) { subjectCounter[schoolClass][subject][key] += 1; } } else { subjectCounter[schoolClass][subject][null] += 1; } } }
对应的测试代码:
const main = require('./main'); test("测试updateSubjectCounter函数", () => { // 初始化测试用的计数器 const testCounter = { "a": { "IFM": { "group1": 0, "group2": 0 } } }; // 调用函数,传入测试计数器 main.updateSubjectCounter(testCounter, "", "a", "IFM"); // 验证结果 expect(testCounter).toEqual({ "a": { "IFM": { "group1": 1, "group2": 1 } } }); });
2. 导出全局变量subjectCounter,测试时直接修改
修改main.js的导出部分,把subjectCounter加入导出列表:
if (typeof module !== 'undefined') { module.exports = { "getTeachers": getTeachers, "getGroups": getGroups, "createSubjectCounter": createSubjectCounter, "updateSubjectCounter": updateSubjectCounter, "subjectCounter": subjectCounter // 新增导出 } }
测试时直接初始化测试用的计数器:
const main = require('./main'); test("测试updateSubjectCounter函数", () => { // 重置为测试用的计数器 main.subjectCounter = { "a": { "IFM": { "group1": 0, "group2": 0 } } }; // 调用函数 main.updateSubjectCounter("", "a", "IFM"); // 验证结果 expect(main.subjectCounter).toEqual({ "a": { "IFM": { "group1": 1, "group2": 1 } } }); });
3. 使用Jest Mock模块,间接控制全局变量
如果不想修改原代码的导出逻辑,可以通过Jest的模块Mock功能,包裹原函数并注入自定义的subjectCounter:
const main = require('./main'); // Mock模块,替换updateSubjectCounter的全局依赖 jest.mock('./main', () => { const originalModule = jest.requireActual('./main'); let mockSubjectCounter = {}; return { ...originalModule, // 重写updateSubjectCounter,让它使用我们的mock计数器 updateSubjectCounter: (group, schoolClass, subject) => { global.subjectCounter = mockSubjectCounter; return originalModule.updateSubjectCounter(group, schoolClass, subject); }, // 提供设置和获取mock计数器的方法 setMockSubjectCounter: (val) => { mockSubjectCounter = val; }, getMockSubjectCounter: () => mockSubjectCounter }; }); test("测试updateSubjectCounter函数", () => { // 设置测试用计数器 main.setMockSubjectCounter({ "a": { "IFM": { "group1": 0, "group2": 0 } } }); // 调用函数 main.updateSubjectCounter("", "a", "IFM"); // 验证结果 expect(main.getMockSubjectCounter()).toEqual({ "a": { "IFM": { "group1": 1, "group2": 1 } } }); });
内容的提问来源于stack exchange,提问作者Semtex
相关产品推荐
相关产品推荐

