
本文介绍通过函数表达式替代函数声明的方式,在不修改原始逻辑的前提下,安全地覆盖全局函数以支持单元测试中的行为模拟。
在 javaScript 单元测试中,常需对依赖的底层函数(如 getData)进行模拟(mock),以隔离被测函数(如 getUsers)的行为。但若目标函数是使用函数声明(function getData() { … })定义的,因其存在提升(hoisting) 且绑定在作用域顶层,直接赋值覆盖(如 getData = mock_getData)在严格模式下可能无效,或因执行时机问题导致未生效。
✅ 正确做法是:统一使用函数表达式定义可模拟的全局函数。这样它们就成为可重赋值的变量,便于在测试前动态替换:
// ✅ 推荐:用函数表达式定义,而非函数声明 var getData = function() { // 实际逻辑:从数据库获取数据 throw new Error('Real database call — should not run in tests'); }; var getUsers = function() { var data = getData(); // 调用的是当前绑定的 getData return data.map(user => ({ ...user, loaded: true })); }; function main() { var users = getUsers(); // ... }
随后,在测试入口处(如 runTests 前),通过简单赋值即可完成模拟:
// ? 测试前:覆盖 getData 为模拟实现 var getData = function() { return [{ id: 0, name: 'Alice' }, { id: 1, name: 'Bob' }]; }; test('getUsers', function() { var users = getUsers(); // 现在调用的是模拟版 getData myAssert(users.length, 2); myAssert(users[0].name, 'Alice'); myAssert(users[1].name, 'Bob'); });
? 关键优势:
立即学习“Java免费学习笔记(深入)”;
- 无需引入外部测试框架(如 Jest 的 jest.mock);
- 零侵入原始业务代码(仅需将 function xxx() {…} 改为 var xxx = function() {…});
- 模拟与真实实现共用同一标识符,语义清晰、维护成本低;
- 支持按需开关(例如结合 _DEBUG = true 统一控制)。
⚠️ 注意事项:
- 所有被模拟的函数必须定义为可变变量(var / let),不可用 const;
- 模拟赋值必须在被测函数首次调用之前执行(推荐放在 runTests() 开头);
- 若模块化环境(ESM)中,全局变量不可写,此时应改用依赖注入或代理模式;
- 生产构建时建议通过打包工具(如 webpack DefinePlugin)自动移除模拟逻辑,避免泄露。
总结:函数表达式 + 可变变量绑定,是最轻量、最可控的原生 javascript 函数模拟方案,特别适合微型测试库或教学/原型场景。