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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 09:25:10