gpt4 book ai didi

javascript - JavaScript/Mocha-如何测试是否已等待函数调用

转载 作者:数据小太阳 更新时间:2023-10-29 04:53:00 24 4
gpt4 key购买 nike

我想编写一个测试,检查我的函数是否使用await关键字调用其他函数。

我希望我的测试失败:

async methodA() {
this.methodB();
return true;
},

我希望我的测试能够 成功:
async methodA() {
await this.methodB();
return true;
},

我也想让 成功地使成为我的测试:
methodA() {
return this.methodB()
.then(() => true);
},

我有一个解决方案,方法是使用 process.nextTick对该方法进行 stub 并强制其在其中返回假 promise ,但这似乎很丑陋,我不想在测试中使用 process.nextTicksetTimeout等。

ugly-async-test.js
const { stub } = require('sinon');
const { expect } = require('chai');

const testObject = {
async methodA() {
await this.methodB();
},
async methodB() {
// some async code
},
};

describe('methodA', () => {
let asyncCheckMethodB;

beforeEach(() => {
asyncCheckMethodB = stub();
stub(testObject, 'methodB').returns(new Promise(resolve => process.nextTick(resolve)).then(asyncCheckMethodB));
});

afterEach(() => {
testObject.methodB.restore();
});

it('should await methodB', async () => {
await testObject.methodA();
expect(asyncCheckMethodB.callCount).to.be.equal(1);
});
});

测试函数调用中是否使用了 await的聪明方法是什么?

最佳答案

TLDR

如果methodA调用await上的methodB,则Promise返回的methodA将无法解析,直到Promise返回的methodB解析为止。

另一方面,如果methodA未在await上调用methodB,则Promise返回的methodA将立即解析Promise返回的methodB是否已解决。

因此,测试methodA是否在await上调用methodB仅仅是测试Promise返回的methodA是否等待Promise返回的methodB解析之后再解决:

const { stub } = require('sinon');
const { expect } = require('chai');

const testObject = {
async methodA() {
await this.methodB();
},
async methodB() { }
};

describe('methodA', () => {
const order = [];
let promiseB;
let savedResolve;

beforeEach(() => {
promiseB = new Promise(resolve => {
savedResolve = resolve; // save resolve so we can call it later
}).then(() => { order.push('B') })
stub(testObject, 'methodB').returns(promiseB);
});

afterEach(() => {
testObject.methodB.restore();
});

it('should await methodB', async () => {
const promiseA = testObject.methodA().then(() => order.push('A'));
savedResolve(); // now resolve promiseB
await Promise.all([promiseA, promiseB]); // wait for the callbacks in PromiseJobs to complete
expect(order).to.eql(['B', 'A']); // SUCCESS: 'B' is first ONLY if promiseA waits for promiseB
});
});



细节

在您的所有三个代码示例中, methodAmethodB都返回 Promise

我将 Promise返回的 methodA称为 promiseA,将 Promise返回的 methodB称为 promiseB

您要测试的是promiseA是否等待解析直到promiseB解析。

首先,让我们看一下如何测试 promiseA不等待 promiseB

测试promiseA是否不等待promiseB

测试否定情况( promiseA不等待 promiseB)的一种简单方法是模拟 methodB以返回 从未解决Promise:

describe('methodA', () => {

beforeEach(() => {
// stub methodB to return a Promise that never resolves
stub(testObject, 'methodB').returns(new Promise(() => {}));
});

afterEach(() => {
testObject.methodB.restore();
});

it('should NOT await methodB', async () => {
// passes if promiseA did NOT wait for promiseB
// times out and fails if promiseA waits for promiseB
await testObject.methodA();
});

});

这是一个非常干净,简单和直接的测试。

如果我们可以返回相反的结果,那就太棒了……如果此测试会使 失败,则返回 true

不幸的是,这不是一个合理的方法,因为 如果promiseA确实是await promiseB,则此测试会使超时。

我们将需要一种不同的方法。

背景信息

在继续之前,这里有一些有用的背景信息:

JavaScript使用 message queue。下一条开始之前的当前消息 runs to completion。运行测试时,测试是当前消息。

ES6引入了 PromiseJobs queue来处理“响应 promise 的解决方案”的工作。 PromiseJobs队列中的所有作业都在当前消息完成之后且下一条消息开始之前运行。

因此,当 Promise解析后,其 then回调将添加到PromiseJobs队列中,并且当当前消息完成时,PromiseJobs中的所有作业将按顺序运行,直到队列为空。
asyncawait只是 syntactic sugar over promises and generators。在 await上调用 Promise本质上将其余函数包装在PromiseJobs中计划的回调中,等待的 Promise解析后再进行调度。

我们需要的是一个测试,该测试将告诉我们 promiseA DID是否等待 promiseB而不超时。

由于我们不希望测试超时,因此 promiseApromiseB 都必须解析。

因此,目标是找出一种方法来判断 promiseA是否正在等待 promiseB,因为它们都在解析。

答案是利用PromiseJobs队列。

考虑一下这个测试:

it('should result in [1, 2]', async () => {
const order = [];
const promise1 = Promise.resolve().then(() => order.push('1'));
const promise2 = Promise.resolve().then(() => order.push('2'));
expect(order).to.eql([]); // SUCCESS: callbacks are still queued in PromiseJobs
await Promise.all([promise1, promise2]); // let the callbacks run
expect(order).to.eql(['1', '2']); // SUCCESS
});
Promise.resolve()返回已解析的 Promise,因此这两个回调将立即添加到PromiseJobs队列中。暂停当前​​消息(测试)以等待PromiseJobs中的作业后,它们将按照添加到PromiseJobs队列中的顺序运行,并且当测试在 await Promise.all之后继续运行时, order数组将按预期包含 ['1', '2']

现在考虑这个测试:

it('should result in [2, 1]', async () => {
const order = [];
let savedResolve;
const promise1 = new Promise((resolve) => {
savedResolve = resolve; // save resolve so we can call it later
}).then(() => order.push('1'));
const promise2 = Promise.resolve().then(() => order.push('2'));
expect(order).to.eql([]); // SUCCESS
savedResolve(); // NOW resolve the first Promise
await Promise.all([promise1, promise2]); // let the callbacks run
expect(order).to.eql(['2', '1']); // SUCCESS
});

在这种情况下,我们从第一个 resolve中保存 Promise,以便稍后使用。由于第一个 Promise尚未解析,因此 then回调不会立即添加到PromiseJobs队列中。另一方面,第二个 Promise已经解决,因此其 then回调已添加到PromiseJobs队列中。一旦发生这种情况,我们将调用保存的 resolve,以便解析第一个 Promise,这会将其 then回调添加到PromiseJobs队列的末尾。一旦当前消息(测试)暂停以等待PromiseJobs中的作业, order数组将按预期包含 ['2', '1']

What is the smart way to test if await was used in the function call?



测试函数调用中是否使用了 await的明智方法是向 thenpromiseA都添加 promiseB回调,然后 延迟解析promiseB 。如果 promiseA等待 promiseB,则其回调将 始终是PromiseJobs队列中的最后。另一方面,如果 promiseA不等待 promiseB,则其回调将在PromiseJobs中首先在队列中排队。

最终解决方案在 TLDR 部分中。

请注意,当 methodA是调用 async上的 awaitmethodB函数时,以及当 methodA是返回链接到 async返回的 PromisePromise的普通(非 methodB)函数时,这种方法都有效(这是期望的,因为同样, async / await只是 Promises和生成器上的语法糖。

关于javascript - JavaScript/Mocha-如何测试是否已等待函数调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54998608/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com