使用 sinon fake 进行单元测试无法解决承诺
Unit test with sinon fake does not resolve promise
我正在学习 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 or async/await in tests. I want to use a 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;
});
});
});
你的_shell.exec
只是一个回调函数,不是Promise。这就是为什么当你伪造 shell.exec 成为一个承诺时,你的 resolve
永远不会被调用。我认为你需要将你的 fakeShell 伪造成这样的东西:
const fakeShell = {
exec: (cmd, options, cb) => {
cb(true, 'pass', null);
}
};
你的函数有点复杂,但没有什么是 sinon 不能用存根处理的。有关详细信息,请参阅 https://sinonjs.org/releases/v1.17.7/stubs/,但您应该在函数之前使用 callsArgOnWith
。
不是将 exec
设置为 return 承诺,而是需要将其设置为存根。这样就可以在遇到的时候使用callsArgOnWith
函数调用回调了。
我已经更改了您的测试,因此它现在通过将伪造的 exec
函数更改为 return 存根 const fakeShell = { exec: sinon.stub() };
并在 [= 之前添加行 fakeShell.exec.callsArgOnWith(2, null, null, 'pass', null)
26=] 你的函数
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;
});
});
});
我正在学习 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 or async/await in tests. I want to use a 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;
});
});
});
你的_shell.exec
只是一个回调函数,不是Promise。这就是为什么当你伪造 shell.exec 成为一个承诺时,你的 resolve
永远不会被调用。我认为你需要将你的 fakeShell 伪造成这样的东西:
const fakeShell = {
exec: (cmd, options, cb) => {
cb(true, 'pass', null);
}
};
你的函数有点复杂,但没有什么是 sinon 不能用存根处理的。有关详细信息,请参阅 https://sinonjs.org/releases/v1.17.7/stubs/,但您应该在函数之前使用 callsArgOnWith
。
不是将 exec
设置为 return 承诺,而是需要将其设置为存根。这样就可以在遇到的时候使用callsArgOnWith
函数调用回调了。
我已经更改了您的测试,因此它现在通过将伪造的 exec
函数更改为 return 存根 const fakeShell = { exec: sinon.stub() };
并在 [= 之前添加行 fakeShell.exec.callsArgOnWith(2, null, null, 'pass', null)
26=] 你的函数
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;
});
});
});