如何在JavaScript中覆写全局函数以实现Mock?
如何Mock顶层定义的函数?
问题场景
我正在开发一个小型测试库,遇到问题:如何Mock顶层定义的函数?以下是完整单文件源码:
function main(){ var users = getUsers(); // ... } function getUsers() { var data = getData() // ... 这里假设原逻辑是返回data对应的用户列表,比如直接返回data return data; } function getData() { // ... get data from database } // test code function mock_getData() { return [{id: 0, name: 'Alice'}, {id: 1, name: 'Bob'}] } test('getUsers', function(){ var users = getUsers() // 如何让getUsers中调用mock_getData而非getData? myAssert(users[0]['name'], 'Alice') myAssert(users[1]['name'], 'Bob') }) // my very small test library var tests = [] function myAssert(a, b) { if (a !== b) { throw new Error()} } function test(name, func) { tests.push({name: name, func: func}) } function runTests() { for (i = 0; i < tests.length; i++) { var current_test = tests[i] try { current_test.func() } catch (err) { console.error(current_test.name) } } }
解决方案
方法1:直接替换全局作用域的getData函数
你的函数都定义在全局作用域下,测试时可以直接把原getData替换成mock函数,测试完成后一定要恢复原函数,避免影响其他测试用例:
test('getUsers', function(){ // 先保存原函数,方便后续恢复 const originalGetData = getData; // 替换为mock函数 getData = mock_getData; try { var users = getUsers() myAssert(users[0]['name'], 'Alice') myAssert(users[1]['name'], 'Bob') } finally { // 不管测试成功还是失败,都恢复原函数 getData = originalGetData; } })
方法2:封装到对象中(更优雅的长期方案)
如果不想依赖全局作用域的替换,可以把相关函数封装到一个模块对象里,这样Mock起来更清晰,也不会污染全局环境:
修改原业务代码:
const DataModule = { main(){ var users = this.getUsers(); // ... }, getUsers() { var data = this.getData() // ... 原逻辑处理 return data; }, getData() { // ... 从数据库获取数据 } }
对应的测试代码:
test('getUsers', function(){ const originalGetData = DataModule.getData; DataModule.getData = mock_getData; try { var users = DataModule.getUsers() myAssert(users[0]['name'], 'Alice') myAssert(users[1]['name'], 'Bob') } finally { DataModule.getData = originalGetData; } })
方法3:给测试库加Mock辅助函数
可以扩展你的小型测试库,添加一个专门的Mock函数,简化重复的保存、替换、恢复操作:
// 扩展你的测试库 function mockGlobalFunc(funcName, mockFunc) { const original = window[funcName]; // 浏览器环境用window,Node环境换成global window[funcName] = mockFunc; // 返回一个恢复函数,调用它就能还原原函数 return () => { window[funcName] = original; }; }
测试时使用这个辅助函数:
test('getUsers', function(){ const restoreGetData = mockGlobalFunc('getData', mock_getData); try { var users = getUsers() myAssert(users[0]['name'], 'Alice') myAssert(users[1]['name'], 'Bob') } finally { restoreGetData(); } })
内容的提问来源于stack exchange,提问作者Wt.N
相关产品推荐
相关产品推荐

