Ava并行测试时Sinon Stub报错:Attempted to wrap已包装函数
问题分析与解决方案
错误原因
当Ava以并行模式运行时,如果同一测试文件内的多个测试被允许并行执行(比如通过配置concurrency或命令行参数开启),不同测试的beforeEach钩子会同时触发。虽然每个测试都持有独立的Sinon沙箱,但它们操作的是Node.js进程内缓存的同一个myModule导出对象——模块在进程内是单例存在的。
当第一个测试的沙箱还未完成myFunction的Stub(或未执行restore),第二个测试的beforeEach就尝试再次包装同一个函数,Sinon就会抛出「Attempted to wrap myFunction which is already wrapped」错误。
而串行模式下,测试按顺序执行,前一个测试的afterEach会先恢复原函数,后一个测试的beforeEach再进行Stub,因此不会出现冲突。
解决方案
1. 限制同一文件内测试串行执行
修改Ava配置,让同一文件内的测试串行运行,同时保持不同测试文件的并行能力:
// ava.config.js export default { concurrency: 1, // 每个文件内仅允许1个测试并行执行 // 也可针对特定文件单独配置 files: [ { pattern: 'test/**/*.test.js', concurrency: 1 } ] };
或直接通过命令行参数执行:
ava --concurrency=1
2. 提前检查并恢复已包装的函数
在beforeEach中先检查目标函数是否已被Sinon包装,若已包装则先恢复,再创建新的Stub:
import sinon from 'sinon'; import * as myModule from '../../../src/myModule'; test.beforeEach(t => { t.context.sandbox = sinon.createSandbox(); // 检查函数是否已被Stub,若已存在则先恢复 const existingStub = sinon.getStub(myModule, 'myFunction'); if (existingStub) { existingStub.restore(); } t.context.sandbox.stub(myModule, 'myFunction').returns(false); }); test.afterEach.always(t => { t.context.sandbox.restore(); });
3. 启用Ava的隔离模式
开启Ava的isolated模式,让每个测试都在独立的Node.js进程中运行。每个进程的模块缓存相互独立,从根本上避免共享模块对象导致的冲突:
// ava.config.js export default { isolated: true };
或通过命令行执行:
ava --isolated
内容的提问来源于stack exchange,提问作者sf8193
相关产品推荐
相关产品推荐

