使用sinon伪造的单元测试无法解决诺言

时间:2018-08-09 14:40:17

标签: node.js promise mocha shelljs

我正在学习nodejs,并为shelljs函数编写了该包装器,该包装器在实践中似乎按预期工作。

/**
 * Wrapper for Shelljs.exec to always return a promise
 *
 * @param  {String} cmd - bash-compliant command string
 * @param  {String} path - working directory of the process
 * @param {Object} _shell - alternative exec function for testing.
 * @returns {String}
 * @throws {TypeError}
 */
function shellExec(cmd, path, _shell = shelljs){
    if( typeof _shell.exec !== "function") throw new TypeError('_shell.exec must be a function');
    return new Promise((resolve, reject) => {
        let options =  { cwd: path, silent: true, asyc: true }
        // eslint-disable-next-line no-unused-vars
        return _shell.exec(cmd, options, (code, stdout, stderr) => {
            // shelljs.exec does not always return a code
            if(stderr) {
                return reject(stderr);
            }
            return resolve(stdout);
        });
    });
}

但是,当我尝试对其进行单元测试时,该功能会超时。我已阅读有关测试中的promisesasync/await异步代码的mochajs文档。我想使用一个我知道有效的sinon fake that returns a promise。 Mocha告诉我错误是该函数未通过错误Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves返回承诺。我想我伪造的伪造品不正确,但是我看不到我应该怎么做。

const { expect, use } = require('chai');
const sinon = require('sinon');
const sinonChai = require("sinon-chai");
const utils = require('../utility/exec');

use(sinonChai);

it('sinon fake should resolve', async () =>{
    const fake = sinon.fake.resolves('resolved');

    const result = await fake();
    expect(result).to.equal('resolved');
});
describe('Utility Functions', () =>{
    describe('shellExec', () =>{
        it('should accept an alternate execute function', async () =>{
            const fakeShell = { exec: sinon.fake.resolves('pass') };
            const result = await utils.shellExec('pwd', 'xyz', fakeShell);
            expect(result).to.equal('pass');
            expect(fakeShell.exec).to.have.been.calledOnce;
        });
    });
});

2 个答案:

答案 0 :(得分:1)

您的_shell.exec只是一个回调函数,它不是一个Promise。这就是为什么当您伪造shell.exec作为承诺时,您的resolve将永远不会被调用的原因。我认为您需要将您的fakeShell伪造成这样的东西:

const fakeShell = { 
   exec: (cmd, options, cb) => {
      cb(true, 'pass', null);
   }
};

答案 1 :(得分:1)

您的功能有些复杂,但是桩无法处理任何sinon。有关更多信息,请参见https://sinonjs.org/releases/v1.17.7/stubs/,但您应该在函数前使用callsArgOnWith

您无需将exec设置为返回承诺,而是需要将其设置为存根。这样,您可以在遇到callsArgOnWith函数时使用该回调函数。

我已经更改了测试,因此现在可以通过更改伪造的exec函数以返回存根const fakeShell = { exec: sinon.stub() };并在运行函数之前添加行fakeShell.exec.callsArgOnWith(2, null, null, 'pass', null)来通过测试

const { expect, use } = require('chai');
const sinon = require('sinon');
const sinonChai = require("sinon-chai");
const utils = require('./main');


use(sinonChai);

it('sinon fake should resolve', async () =>{
    const fake = sinon.fake.resolves('resolved');

    const result = await fake();
    expect(result).to.equal('resolved');
});
describe('Utility Functions', () =>{
    describe('shellExec', () =>{
        it('should accept an alternate execute function', async () =>{
            const fakeShell = { exec: sinon.stub() };
            fakeShell.exec.callsArgOnWith(2, null, null, 'pass', null)
            const result = await utils.shellExec('pwd', 'xyz', fakeShell);            
            expect(result).to.equal('pass');
            expect(fakeShell.exec).to.have.been.calledOnce;
        });
    });
});