Here是此问题的存储库。
我有一个新创建的react-native项目(对于这个问题,我认为它是React还是React-Native并不重要)。我只有一个组件App.js
:
import React, { Component } from 'react';
import { View } from 'react-native';
import actions from './actions';
export class App extends Component {
async componentDidMount() {
console.log('In CDM');
await actions.funcOne();
await actions.funcTwo();
console.log('Finished CDM');
}
render() {
return <View />;
}
}
以下是该组件从actions.js
导入的两个函数:
const funcOne = async () => {
console.log('One');
};
const funcTwo = async () => {
console.log('Two');
};
export default { asyncOne: funcOne, asyncTwo: funcTwo };
这是我编写的测试:
import React from 'react';
import { App } from '../App';
import renderer from 'react-test-renderer';
import actions from '../actions';
const spyOne = jest.spyOn(actions, 'funcOne');
const spyTwo = jest.spyOn(actions, 'funcTwo');
describe('App ', () => {
test('does async stuff in expected order', async () => {
console.log('Starting test');
const tree = await renderer.create(<App />);
console.log('About to expect');
expect(spyOne).toHaveBeenCalled();
console.log('Expect one to have been called');
expect(spyTwo).toHaveBeenCalled();
console.log('Expect two to have been called');
expect(tree).toMatchSnapshot();
});
});
可以看出,在expect
中执行函数funcTwo
之前,将调用第二个componentDidMount
断言。
我实际上想要实现的是,我有一个更加复杂的组件,该组件在componentDidMount
中执行异步功能(例如,进行API调用)。我希望测试创建组件树,并断言该组件确实确实调用了相关功能。
我实际上找到了一个“解决方案”(它使我的测试通过,并且console.logs以正确的顺序显示,但是我不知道它为什么起作用。解决方案是在其中添加行await (() => new Promise(setImmediate))();
await renderer.create
行之后的测试文件。
**因此,我不想要一个解决方案(尽管如果您有理想的解决方案,请提供它)。我想知道这里发生了什么,为什么原始代码无法按预期工作? **
答案 0 :(得分:1)
async / await
只是针对Promise和Generator的语法糖。
调用await
时,实际上是将其余功能放在等待的then
上的Promise
中。
这意味着Promise
解决后,其余功能将添加到PromiseJobs队列中。
Promise
回调在当前消息完成后运行 ......,这意味着任何同步代码都将在回调有机会运行之前完成。
在这种情况下,此行运行:
await actions.funcOne();
...将同时调用funcOne
。它会立即解决,因此将componentDidMount
的其余部分放入PromiseJobs队列中,然后执行返回测试。 (请注意,在await
上调用renderer.create
不会等待Promise
返回的componentDidMount
。)
其余测试是同步的,因此它运行第一个通过的expect
,然后运行第二个expect
,该测试失败,因为componentDidMount
的其余部分仍在PromiseJobs队列中等待
要使测试通过,只需给在PromiseJobs中排队的回调提供运行的机会。
如您所见,可以使用以下行完成此操作:
await (() => new Promise(setImmediate))();
...但更容易的是await
解决的Promise
:
await Promise.resolve();
这会将其余的 test 排队在PromiseJobs队列的后面,该队列位于将调用actions.funcTwo
的回调后面,并且测试将通过。
以下是一个稍微简化的示例来演示:
import * as React from 'react';
import renderer from 'react-test-renderer';
const f1 = jest.fn();
const f2 = jest.fn();
class App extends React.Component {
async componentDidMount() {
await f1();
await f2();
}
render() { return null; }
}
test('does async stuff in expected order', async () => {
const tree = renderer.create(<App />);
expect(f1).toHaveBeenCalled(); // Success!
await Promise.resolve(); // <= let any callbacks in PromiseJobs run
expect(f2).toHaveBeenCalled(); // Success!
});