回调中方法的 Sinon 加密存根

Sinon crypto stub for method within a callback

我正在尝试测试一个使用 nodejs 加密库生成随机名称的简单函数。我正在使用 sinon 在 pseudoRandomBytes 的回调中存根一个方法调用,但存根似乎没有被调用。示例:

getFileName.js

const crypto = require('crypto');

module.exports = (req, file, cb) => {
  crypto.pseudoRandomBytes(32, (err, raw) => {
    try{
      cb(err, err ? undefined : crypto.createHash('MD5').update(raw).digest('hex'));
    } catch(err) {
      cb(err);
    }
  });
};

测试(运行 在 mocha 中)

it('Crypto Error: createHash', function () {
  const crypto = require('crypto');
  const expectedError = new Error('stub error occurred');
  let cryptoStub = sinon.stub(crypto, 'createHash').throws(expectedError);
  let callback = sinon.spy();

  getFileName(null, null, callback);

  cryptoStub.restore();
  sinon.assert.calledWith(callback, expectedError);
});

我希望一旦 createHash 被调用,上面的测试就会抛出。如果我将 crypto.createHash 调用移到回调之外(在 pseudoRandomNumber 调用之前),它就可以正常工作。我有点新手,所以我对 sinon 和 nodejs 正在做什么的基本理解可能是完全错误的。任何帮助将不胜感激。

问题在于 crypto.pseudoRandomBytes() 是一个异步函数,因此您的其余测试代码会在您的回调之前执行。这样,您的存根就会在您的函数实际使用之前恢复。

为了使其正常工作,您应该更新您的 getFileName.js 以便它 returns 一个承诺 - 这样您就可以 await it

module.exports = (req, file, cb) => {
    return new Promise((resolve, reject) => {
        crypto.pseudoRandomBytes(32, (err, raw) => {
            try{
                cb(err, err ? undefined : crypto.createHash('MD5').update(raw).digest('hex'));
                resolve();
            } catch(err) {
              reject(cb(err));
            }
        });
    });
};

然后在你的测试中

// added async
it('Crypto Error: createHash', async () => {
  const crypto = require('crypto');
  const expectedError = new Error('stub error occurred');
  let cryptoStub = sinon.stub(crypto, 'createHash').throws(expectedError);
  let callback = sinon.spy();

  await getFileName(null, null, callback);
  // once we are here, the callback has already been executed and the promise that getFileName resolved.
  cryptoStub.restore();
  sinon.assert.calledWith(callback, expectedError);
});

似乎 createHash() 没有被调用的原因是因为异步函数导致您在回调调用完成之前进行断言。

async/await 的承诺会奏效。另一种不涉及更改模块以使用 promise 的方法是在回调中进行断言。

it('Crypto Error: createHash', function (done) {
  const crypto = require('crypto');
  const expectedError = new Error('stub error occurred');
  let cryptoStub = sinon.stub(crypto, 'createHash').throws(expectedError);

  getFileName(null, null, function (err, hash) {
    sinon.assert.match(err, expectedError);
    cryptoStub.restore();
    done();
  });
});

这样,您可以检查调用回调时是否出现预期错误。确认这一点的一种方法是,您可以将第 4 行更改为 .throws('some other error'),测试将失败。