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

如何使用Sinon.JS存根已被promisify的方法?修复测试失效问题

Hey there! Let's figure out why your Sinon stub isn't working and fix it up.

The Problem

Your custom.js runs util.promisify(fs.access) the moment the module loads. This creates a Promise-style function that's permanently tied to the original fs.access method. When you stub fs.access in your tests later, that pre-made promisified function doesn't pick up the stub—it still calls the real fs.access, which is why your tests are failing.

Fix 1: Use proxyquire to Replace Dependencies (No Code Changes to custom.js)

proxyquire lets you swap out a module's dependencies when you load it, so we can make sure util.promisify returns our stubbed function instead of wrapping the real fs.access.

First, install it:

npm install proxyquire --save-dev

Then update your test file:

const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const sinon = require('sinon')
const proxyquire = require('proxyquire')

chai.use(chaiAsPromised)
const expect = chai.expect

describe('custom', () => {
  let sandbox = sinon.createSandbox()
  let custom
  let stubbedAccess

  beforeEach(() => {
    // Create a stubbed Promise-style access function
    stubbedAccess = sandbox.stub()
    // Load custom.js and replace util.promisify to return our stub
    custom = proxyquire('./custom', {
      util: { promisify: () => stubbedAccess }
    })
  })

  afterEach(() => sandbox.restore())

  it('returns "File exists: true" when the file is accessible', () => {
    stubbedAccess.resolves() // Simulate successful access
    return expect(custom()).to.eventually.equal('File exists: true')
  })

  it('returns "File exists: false" when the file doesn\'t exist', () => {
    const noFileError = new Error('No such file or directory')
    noFileError.code = 'ENOENT'
    stubbedAccess.rejects(noFileError) // Simulate missing file error
    return expect(custom()).to.eventually.equal('File exists: false')
  })

  it('throws an error for non-ENOENT issues', () => {
    const permError = new Error('Permission denied')
    permError.code = 'EACCES'
    stubbedAccess.rejects(permError) // Simulate permission error
    return expect(custom()).to.be.rejectedWith(permError)
  })
})

Fix 2: Refactor custom.js for Dependency Injection

If you don't want to add another library, you can adjust your module to let tests inject a custom access function. This makes your code more testable long-term.

Update custom.js:

const fs = require('fs')
const util = require('util')

// Default to promisified fs.access, but let tests override it
let access = util.promisify(fs.access)

// Expose a method to set a custom access function for testing
function setAccess(customAccess) {
  access = customAccess
}

const custom = () => {
  return access('myfile', fs.constants.R_OK)
    .then(() => true)
    .catch(err => {
      if (err.code !== 'ENOENT') throw err
      return false
    })
    .then(fileExists => `File exists: ${fileExists}`)
}

module.exports = { custom, setAccess }

Then update your tests:

const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const sinon = require('sinon')
const { custom, setAccess } = require('./custom')

chai.use(chaiAsPromised)
const expect = chai.expect

describe('custom', () => {
  let sandbox = sinon.createSandbox()
  let stubbedAccess

  beforeEach(() => {
    stubbedAccess = sandbox.stub()
    setAccess(stubbedAccess) // Inject our stub into the module
  })

  afterEach(() => sandbox.restore())

  it('returns "File exists: true" when the file is accessible', () => {
    stubbedAccess.resolves()
    return expect(custom()).to.eventually.equal('File exists: true')
  })

  it('returns "File exists: false" when the file doesn\'t exist', () => {
    const noFileError = new Error('No such file or directory')
    noFileError.code = 'ENOENT'
    stubbedAccess.rejects(noFileError)
    return expect(custom()).to.eventually.equal('File exists: false')
  })
})

Both of these approaches will make sure your tests use the stubbed function instead of the real fs.access. The first one keeps your business code untouched, while the second makes your module inherently more testable.

内容的提问来源于stack exchange,提问作者Eduardo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:34:20