【问题标题】:Unit test with sinon fake does not resolve promise使用 sinon fake 进行单元测试无法解决承诺
【发布时间】:2019-01-17 02:07:12
【问题描述】:

我正在学习 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 文档,promisesasync/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;
        });
    });
});

【问题讨论】:

  • 我发现 Peter 和 Dat Tran 的回答都非常有帮助,但 Peter 的回答更符合实际测试。

标签: node.js promise mocha.js shelljs


【解决方案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;
        });
    });
});

【讨论】:

  • 我阅读了您提供的链接中的文档,但对我来说一清二楚。你能解释一下callsArgOnWith(2, null, null, 'pass', null)中的参数吗?
  • stub.callsArgOnWith(index, context, arg1, arg2, ...); Index - exec 函数的哪个参数是回调。 0 indexed, context - 你想通过的任何作用域,剩下的就是你传递给回调的参数
【解决方案2】:

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

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

【讨论】:

  • 你是说我这里不需要使用任何sinon对象,我只是传入新的fakeShell,测试一下结果?我没有看到 _shell 是一个回调,但当我使用该函数时,它会响应 .then/.catch
  • shell.exec 是一个回调函数。当它完成时,它使用来自外部承诺的解析来解决它。这就是函数的意义。将回调包装到 Promise 中。
  • _shell.exec(cmd, options, (code, stdout, stderr) => { => cmd是第一个参数,options是第二个,(code, stdout, stderr) => {}是第三个参数,实际上是_shell.exec之后调用的回调函数完成。
  • 我现在看到在这种情况下我根本不需要 shelljs,因为 exec 的实用程序 promisfy() 方法达到了相同的效果。参数一样!
猜你喜欢
  • 2017-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-23
  • 2019-08-21
  • 2017-10-13
  • 2018-10-30
  • 2023-03-27
相关资源
最近更新 更多