Sinon 忽略后续的异步调用
Sinon ignores subsequent async call
我需要验证所有交易调用是否成功。
这是示例:
module.js
const functionUnderTest = async (helper) => {
await helper.transaction(async (transaction) => {
await helper.doSomething();
await helper.doSomething2();
await helper.expectedToBeCalled();
console.log("done");
});
};
module.exports = {
functionUnderTest,
};
问题来了,出于某种原因,对于 sinon,我无法验证最后一次调用是否已完成,但始终打印消息“完成”。
这是输出的样子
$ npm run test
> sinon-test@1.0.0 test
> mocha
module
✔ the former function should be called
done
1) the latter function should be called
done
1 passing (7ms)
1 failing
1) module
the latter function should be called:
AssertionError: expected stub to have been called at least once, but it was never called
at Context.<anonymous> (test/module.test.js:31:48)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
测试本身如下所示
helpers/chai.js
const chaiAsPromised = require("chai-as-promised");
const chaiSinon = require("sinon-chai");
chai.use(chaiSinon);
chai.use(chaiAsPromised);
module.exports = { expect: chai.expect };
test/module.test.js
const { expect } = require("./helpers/chai");
const sinon = require("sinon");
const { functionUnderTest } = require("../module");
describe("module", function () {
let sandbox;
let mockHelper;
const transaction = {};
beforeEach(function () {
sandbox = sinon.createSandbox();
mockHelper = {
transaction: sandbox.stub().callsArgWithAsync(0, transaction),
doSomething: sandbox.stub().resolves({}),
doSomething2: sandbox.stub().resolves({}),
expectedToBeCalled: sandbox.stub().resolves({}),
};
});
afterEach(function () {
sandbox.restore();
});
it("the former function should be called", async function () {
await functionUnderTest(mockHelper);
expect(mockHelper.doSomething).to.be.called;
});
it("the latter function should be called", async function () {
await functionUnderTest(mockHelper);
expect(mockHelper.expectedToBeCalled).to.be.called;
});
});
为了使其完整且易于测试,我创建了一个您可以使用的存储库:https://github.com/vichugunov/sinon-test
我的问题是:
- 怎么可能?
- 我做错了什么?
P.S。如果调用 await helper.doSomething2()
被注释掉,则测试通过
您误解了存根的异步版本 callsArg*
和 yields*
。看到这个 PR,区别在于异步版本 callsArg*
将:
- In Node environment the callback is deferred with
process.nextTick
.
- In a browser the callback is deferred with
setTimeout(callback, 0)
.
它不等待异步回调函数完成。 async/await
在测试用例中只能等待await helper.transaction()
异步函数完成,不能等待其异步回调
callsArg*
的同步版本会立即调用回调。你可以看看这个issue,什么时候应该使用异步版本callsArg*
。
因此,当测试用例执行断言时,helper.expectedTobecalled()
等异步函数没有完成
您应该使用 callsFake()
为 helper.transaction()
方法提供异步存根回调并使用 async/await
.
调用它
例如
module.js
:
const functionUnderTest = async (helper) => {
await helper.transaction(async (transaction) => {
await helper.doSomething();
await helper.doSomething2();
await helper.expectedToBeCalled();
console.log('done');
});
};
module.exports = { functionUnderTest };
module.test.js
:
const sinon = require('sinon');
const { functionUnderTest } = require('./module');
describe('module', function () {
let sandbox;
let mockHelper;
const transaction = {};
beforeEach(function () {
sandbox = sinon.createSandbox();
mockHelper = {
transaction: sandbox.stub().callsFake(async (callback) => {
await callback(transaction);
}),
doSomething: sandbox.stub().resolves({}),
doSomething2: sandbox.stub().resolves({}),
expectedToBeCalled: sandbox.stub().resolves({}),
};
});
afterEach(function () {
sandbox.restore();
});
it('the former function should be called', async function () {
await functionUnderTest(mockHelper);
sinon.assert.calledOnce(mockHelper.doSomething);
});
it('the latter function should be called', async function () {
await functionUnderTest(mockHelper);
sinon.assert.calledOnce(mockHelper.expectedToBeCalled);
});
});
测试结果:
module
done
✓ the former function should be called
done
✓ the latter function should be called
2 passing (5ms)
我需要验证所有交易调用是否成功。 这是示例:
module.js
const functionUnderTest = async (helper) => {
await helper.transaction(async (transaction) => {
await helper.doSomething();
await helper.doSomething2();
await helper.expectedToBeCalled();
console.log("done");
});
};
module.exports = {
functionUnderTest,
};
问题来了,出于某种原因,对于 sinon,我无法验证最后一次调用是否已完成,但始终打印消息“完成”。
这是输出的样子
$ npm run test
> sinon-test@1.0.0 test
> mocha
module
✔ the former function should be called
done
1) the latter function should be called
done
1 passing (7ms)
1 failing
1) module
the latter function should be called:
AssertionError: expected stub to have been called at least once, but it was never called
at Context.<anonymous> (test/module.test.js:31:48)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
测试本身如下所示
helpers/chai.js
const chaiAsPromised = require("chai-as-promised");
const chaiSinon = require("sinon-chai");
chai.use(chaiSinon);
chai.use(chaiAsPromised);
module.exports = { expect: chai.expect };
test/module.test.js
const { expect } = require("./helpers/chai");
const sinon = require("sinon");
const { functionUnderTest } = require("../module");
describe("module", function () {
let sandbox;
let mockHelper;
const transaction = {};
beforeEach(function () {
sandbox = sinon.createSandbox();
mockHelper = {
transaction: sandbox.stub().callsArgWithAsync(0, transaction),
doSomething: sandbox.stub().resolves({}),
doSomething2: sandbox.stub().resolves({}),
expectedToBeCalled: sandbox.stub().resolves({}),
};
});
afterEach(function () {
sandbox.restore();
});
it("the former function should be called", async function () {
await functionUnderTest(mockHelper);
expect(mockHelper.doSomething).to.be.called;
});
it("the latter function should be called", async function () {
await functionUnderTest(mockHelper);
expect(mockHelper.expectedToBeCalled).to.be.called;
});
});
为了使其完整且易于测试,我创建了一个您可以使用的存储库:https://github.com/vichugunov/sinon-test
我的问题是:
- 怎么可能?
- 我做错了什么?
P.S。如果调用 await helper.doSomething2()
被注释掉,则测试通过
您误解了存根的异步版本 callsArg*
和 yields*
。看到这个 PR,区别在于异步版本 callsArg*
将:
- In Node environment the callback is deferred with
process.nextTick
.- In a browser the callback is deferred with
setTimeout(callback, 0)
.
它不等待异步回调函数完成。 async/await
在测试用例中只能等待await helper.transaction()
异步函数完成,不能等待其异步回调
callsArg*
的同步版本会立即调用回调。你可以看看这个issue,什么时候应该使用异步版本callsArg*
。
因此,当测试用例执行断言时,helper.expectedTobecalled()
等异步函数没有完成
您应该使用 callsFake()
为 helper.transaction()
方法提供异步存根回调并使用 async/await
.
例如
module.js
:
const functionUnderTest = async (helper) => {
await helper.transaction(async (transaction) => {
await helper.doSomething();
await helper.doSomething2();
await helper.expectedToBeCalled();
console.log('done');
});
};
module.exports = { functionUnderTest };
module.test.js
:
const sinon = require('sinon');
const { functionUnderTest } = require('./module');
describe('module', function () {
let sandbox;
let mockHelper;
const transaction = {};
beforeEach(function () {
sandbox = sinon.createSandbox();
mockHelper = {
transaction: sandbox.stub().callsFake(async (callback) => {
await callback(transaction);
}),
doSomething: sandbox.stub().resolves({}),
doSomething2: sandbox.stub().resolves({}),
expectedToBeCalled: sandbox.stub().resolves({}),
};
});
afterEach(function () {
sandbox.restore();
});
it('the former function should be called', async function () {
await functionUnderTest(mockHelper);
sinon.assert.calledOnce(mockHelper.doSomething);
});
it('the latter function should be called', async function () {
await functionUnderTest(mockHelper);
sinon.assert.calledOnce(mockHelper.expectedToBeCalled);
});
});
测试结果:
module
done
✓ the former function should be called
done
✓ the latter function should be called
2 passing (5ms)