我正在尝试测试具有异步componentDidMount
。
承诺本身不需要被嘲笑,它不一定是访问外部内容,大多只是道具的包装。
但是,为了测试它,我需要使用wrapper.update()
4次,这对我来说似乎很奇怪。
解决方案:
一切都不适合我。
这是我的测试的样子(目前有效,但这个解决方案根本不优雅,而且不太可扩展):
import * as React from 'react'
import { shallow, mount } from 'enzyme'
import LargeSelector from './LargeSelector'
describe('<LargeSelector />', async () => {
const componentDidMountSpy = jest.spyOn(LargeSelector.prototype, 'componentDidMount')
describe('search', async () => {
it('should save initial response in cache', async () => {
const wrapper = await shallow(<LargeSelector query={async (search) => ['search:' + search]} />)
// WHY DO I NEED 4 UPDATES???
await wrapper.update()
await wrapper.update()
await wrapper.update()
await wrapper.update()
expect(LargeSelector.prototype.componentDidMount).toHaveBeenCalledTimes(1) // works fine
// these 2 only pass the expectation if I call wrapper.update() no less than 4 times
expect(wrapper.state()).toHaveProperty('options', ['search:'])
expect(wrapper.state()).toHaveProperty('initialOptions', ['search:'])
})
})
})
以下是componentDidMount
和filterResults
的实现(在前者中调用):
public async componentDidMount() {
if (this.props.searchOnInit) {
const results = await this.filterResults('', [])
if (this.props.cacheInitialResponse) {
this.setState({ initialOptions: results })
}
}
}
private async filterResults(search: string, filters: IFilter[]) {
const results = await this.props.query(search, filters)
this.setState({ options: results })
return results
}
答案 0 :(得分:1)
奇怪的行为可能是因为您使用了async
来实现componentDidMount
。
目前,所有React渲染过程都是同步的,因此与渲染流相关的所有内容都需要视为同步。目前也是如此,React团队正在开发一种突破性更改功能,以允许async rendering。
但是!即使在此功能可用之后,请记住componentDidMount
lifecycle hook仍然是同步的,而其他所有其他人也会挂钩,因此请务必注意React不会等待任何Promise内部解析钩子。
如果符合您的使用案例,您可以在componentDidMount
内启动Promise,并让解析后的结果更改状态。但是生命周期钩子会在它结束之前完成,这会影响你的测试用例,因为测试需要等待解析的Promise的结果在断言之前被处理,你可以使用jest.runAllTicks()来保证这种行为。
答案 1 :(得分:1)
我正面临着完全相同的问题。 问题在于,测试不会等待承诺实现。我的解决方案是使用Jest提供的done回调作为测试结束的信号。
赞:
it('wait async code before assert something', (doneCallback) => {
const wrapper = shallow(<Component />);
setImediate(() => {
expect(wrapper.find('.async').length).toBe(1);
doneCallback();
});
});
答案 2 :(得分:1)
enzyme-async-helpers在解决此类问题方面给了我很多帮助。
您可以通过添加loading
状态然后执行类似的操作来轻松使其工作:
import * as React from 'react';
import { shallow, mount } from 'enzyme';
import LargeSelector from './LargeSelector';
import { waitForState } from 'enzyme-async-helpers';
describe('<LargeSelector />', async () => {
describe('search', async () => {
it('should save initial response in cache', async () => {
const wrapper = await shallow(<LargeSelector query={async (search) => ['search:' + search]} />);
await waitForState(wrapper, state => state.loading === false);
expect(LargeSelector.prototype.componentDidMount).toHaveBeenCalledTimes(1);
expect(wrapper.state()).toHaveProperty('options', ['search:']);
expect(wrapper.state()).toHaveProperty('initialOptions', ['search:']);
});
});
});
和:
this.setState({ initialOptions: results })
必须更新为:
this.setState({ initialOptions: results, loading: false })