代码之家  ›  专栏  ›  技术社区  ›  Todd

sinon fake的单元测试无法解决承诺

  •  0
  • Todd  · 技术社区  · 6 年前

    我正在学习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);
            });
        });
    }
    

    但是,当我尝试对它进行单元测试时,函数会超时。我读过关于异步代码的mochajs文档, promises async/await 在测试中。我想用一个 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 回复  |  直到 6 年前
        1
  •  1
  •   Peter Grainger    6 年前

    你的功能有点复杂,但是没有什么是sinon不能处理的。见 https://sinonjs.org/releases/v1.17.7/stubs/ 要了解更多信息,但您应该使用的是 callsArgOnWith 在函数之前。

    而不是设置 exec 若要退回承诺,您需要将其设置为存根。这样,您可以使用 callsArgOnWith公司 遇到时的函数。

    我改变了你的测试所以现在通过了 执行官 函数返回存根 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;
            });
        });
    });
    
        2
  •  1
  •   Dat Tran    6 年前

    你的 _shell.exec 只是一个回调函数,不是承诺。这就是为什么当你假装shell.exec是一个承诺时 resolve 永远不会被召唤。我觉得你应该假装你的假货是这样的:

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