You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:10:24