使用Mocha/Sinon为TypeScript模拟marantz-avr依赖失败求助
解决MarantzPlugin单元测试中模拟marantz-avr库的问题
你的测试代码里有几个关键问题导致模拟没生效,还有模块找不到的错误,我来一步步帮你解决:
1. 当前测试的核心问题
- 你错误地尝试stub
AVReceiver.prototype.AVReceiver——这根本不是构造函数的正确位置,构造函数本身是模块导出的顶级函数,不是原型上的属性。 - 直接用
require导入真实模块后,CommonJS的模块缓存机制会让MarantzPlugin依然使用原始的AVReceiver,你的stub无法覆盖它。 - 测试里调用了
marantz.setPowerState('on'),但你的类里实际暴露的方法是turnPowerOn,这是个小笔误。
2. 推荐解决方案:用proxyquire替换依赖
proxyquire是专门用来在CommonJS环境中替换模块依赖的工具,它能让你在加载MarantzPlugin时,把真实的marantz-avr替换成我们的模拟对象,完美解决模块缓存的问题。
步骤1:安装依赖
npm install proxyquire sinon --save-dev
步骤2:编写正确的测试代码
import { suite, test, before } from 'mocha-typescript'; import * as assert from 'assert'; import * as sinon from 'sinon'; import * as proxyquire from 'proxyquire'; // 定义模拟的AVReceiver类型(可选,让TypeScript不报错) type MockAVReceiver = { setPowerState: sinon.SinonStub<['on' | 'off'], Promise<any>>; }; @suite class MarantzPluginTest { private marantzPlugin: any; private mockAVReceiverConstructor: sinon.SinonStub<[string], MockAVReceiver>; private mockSetPowerState: sinon.SinonStub; before(() => { // 1. 创建setPowerState的stub,注意要返回Promise(原方法是异步的) this.mockSetPowerState = sinon.stub().resolves({ success: true }); // 2. 创建AVReceiver构造函数的stub,返回包含mock方法的实例 this.mockAVReceiverConstructor = sinon.stub().returns({ setPowerState: this.mockSetPowerState }); // 3. 用proxyquire加载MarantzPlugin,替换marantz-avr的导出 const MarantzPlugin = proxyquire('./', { 'marantz-avr': this.mockAVReceiverConstructor }).default; // 4. 实例化插件 this.marantzPlugin = new MarantzPlugin({ id: 'MARANTZ', settings: { ip: '192.168.178.2' } }); }); @test 'should call setPowerState with "on" when turnPowerOn is invoked'() { // 调用类的方法 this.marantzPlugin.turnPowerOn(); // 验证构造函数是否用正确的IP被调用 assert.ok(this.mockAVReceiverConstructor.calledWith('192.168.178.2')); // 验证setPowerState是否被调用,参数是'on' assert.ok(this.mockSetPowerState.calledWith('on')); } }
3. 为什么之前的方法会报错?
你提到参考了Sinon的链接接缝文档后出现Cannot find module 'marantz-avr'错误,大概率是因为你在替换模块时没有正确设置模块路径,或者你的TypeScript配置(比如moduleResolution)影响了模块查找。而proxyquire会自动处理模块路径的问题,不需要手动修改模块加载逻辑,更适合这种场景。
4. 额外注意点
- 原类中的
turnPowerOn方法里,then的写法有点问题:then(res => res, error => console.error(error))其实可以简化为.catch(error => console.error(error)),而且如果要测试错误场景,你可以让mock的setPowerStatereject,然后验证错误处理逻辑。 - 如果你不想用
proxyquire,也可以用rewire来修改MarantzPlugin内部的require变量,但proxyquire的写法更直观。
内容的提问来源于stack exchange,提问作者Bas van Dijk
相关产品推荐
相关产品推荐

