Крипто-заглушка Sinon для метода в обратном вызове - PullRequest
2 голосов
/ 04 апреля 2019

Я пытаюсь протестировать простую функцию, которая генерирует случайное имя, используя криптографическую библиотеку 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);
    }
  });
};

Test (выполняется в 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, может быть совершенно неверным.Любая помощь будет высоко ценится.

Ответы [ 2 ]

1 голос
/ 04 апреля 2019

Причина, по которой кажется, что createHash() не был вызван, была в том, что вы делали утверждение до завершения обратного вызова из-за асинхронной функции.

Обещание с async / await будет работать.Другой метод, который не предусматривает изменение вашего модуля для использования обещания, заключается в том, чтобы сделать ваши утверждения в обратном вызове.

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'), и проверка не будет выполнена.

1 голос
/ 04 апреля 2019

Проблема в том, что crypto.pseudoRandomBytes () является асинхронной функцией, поэтому остальная часть вашего тестового кода выполняется перед вашим обратным вызовом. Таким образом, ваша заглушка восстанавливается до того, как ваша функция действительно ее использует.

Для того, чтобы заставить его работать должным образом, вы должны обновить getFileName.js, чтобы он возвращал обещание - таким образом, вы можете ждать его

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);
});
...