Node.js中用Sinon mock convert-excel-to-json遇ESM stub错误求解决
问题描述
我在Node.js项目中使用Mocha作为测试环境,Sinon来存根(stub)和模拟(mock)依赖。现有代码如下:
Factory.js
import fs from 'fs' class Factory{ create = async(options) => { if(fs.existsSync(options.wokerPath)){ const {Worker} = await import(options.wokerPath); const worker = new Worker(options); const result = await worker.process(); } } }
Worker.js
import { RepoWorker } from "../RepoWorker.js"; import excelToJson from "convert-excel-to-json"; class Worker extends RepoWorker { constructor(options) { super(options); } process = async () => { try{ const result = excelToJson({ sourceFile: this.inputPath, header: { rows: 1 }, columnToKey: { '*': '{{columnHeader}}' } }); return result; } catch(ex){ this.logger.console.error("Error inside Generic ExcelParser",ex); console.log(ex); } } } export { Worker }
我已成功用Sinon存根fs.existsSync:
Sinon.stub(fs, 'existsSync').callsFake((inputPath) => { if(inputPath == 'TestPath'){ return true; } })
现在想mock excelToJson使其返回模拟数据,尝试以下代码后报错Type Error: ES Modules Cannot be stubbed:
import * as ExcelToJsonModule from 'convert-excel-to-json'; sinon.stub(ExcelToJsonModule, 'default').callsFake((options) => { if (options.sourceFile === 'TestPath') { return { Sheet1: [ { column1: 'value1', column2: 'value2' }, { column1: 'value3', column2: 'value4' } ] }; } });
解决方案
ES模块的导出是只读的,无法直接用Sinon存根默认导出,可通过以下两种方法解决:
方法一:依赖注入改造Worker类
修改Worker类,允许传入excelToJson作为可选参数,测试时直接传入mock函数即可:
修改后的Worker.js
import { RepoWorker } from "../RepoWorker.js"; import excelToJson from "convert-excel-to-json"; class Worker extends RepoWorker { constructor(options, excelConverter = excelToJson) { super(options); this.excelToJson = excelConverter; // 注入转换器 } process = async () => { try{ const result = this.excelToJson({ sourceFile: this.inputPath, header: { rows: 1 }, columnToKey: { '*': '{{columnHeader}}' } }); return result; } catch(ex){ this.logger.console.error("Error inside Generic ExcelParser",ex); console.log(ex); } } } export { Worker }
测试用例写法
// 创建mock的excelToJson函数 const mockExcelToJson = sinon.fake((options) => { if (options.sourceFile === 'TestPath') { return { Sheet1: [ { column1: 'value1', column2: 'value2' }, { column1: 'value3', column2: 'value4' } ] }; } }); // 创建Worker时传入mock函数 const worker = new Worker({ inputPath: 'TestPath' }, mockExcelToJson); // 执行测试 const result = await worker.process(); // 断言结果 assert.deepEqual(result, { Sheet1: [{ column1: 'value1', column2: 'value2' }, { column1: 'value3', column2: 'value4' }] });
这种方法降低了模块耦合度,是最符合代码设计原则的方案。
方法二:使用proxyquire-esm替换模块
如果不想修改原代码,可使用proxyquire-esm替换convert-excel-to-json模块:
安装依赖
npm install proxyquire-esm --save-dev
测试用例写法
import proxyquire from 'proxyquire-esm'; // 定义mock的excelToJson const mockExcelToJson = sinon.fake((options) => { if (options.sourceFile === 'TestPath') { return { Sheet1: [ { column1: 'value1', column2: 'value2' }, { column1: 'value3', column2: 'value4' } ] }; } }); // 使用proxyquire加载Worker模块,替换目标依赖 const { Worker } = proxyquire('./Worker.js', { 'convert-excel-to-json': mockExcelToJson }); // 后续测试逻辑 const worker = new Worker({ inputPath: 'TestPath' }); const result = await worker.process(); // 断言结果 assert.deepEqual(result, { Sheet1: [{ column1: 'value1', column2: 'value2' }, { column1: 'value3', column2: 'value4' }] });
注意:使用该方案时,需确保测试环境正确处理ES模块,可在Mocha启动时添加--loader esm参数,或在package.json中配置type: module。
内容的提问来源于stack exchange,提问作者Saurav Khewal
相关产品推荐
相关产品推荐

