Я пытаюсь протестировать компонент React с асинхронным 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
}



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Странное поведение, вероятно, связано с тем, что вы использовали async для реализации componentDidMount.
В настоящее время весь процесс рендеринга React является синхронным, поэтому все, что связано с его потоком рендеринга, следует рассматривать как синхронное. И случается, что в настоящее время команда React также разрабатывает функцию критических изменений, чтобы разрешить асинхронный рендеринг.
Но! Даже после того, как эта функция станет доступной, имейте в виду, что componentDidMountкрючок жизненного цикла по-прежнему будет синхронным, и все остальные перехватчики тоже, поэтому важно знать, что React не будет ждать разрешения какого-либо обещания внутри перехватчиков.
Вы можете запустить Promise внутри componentDidMount, если он соответствует вашему варианту использования, и позволить разрешенному результату изменить состояние. Но крючок жизненного цикла завершится до того, как он разрешится, это повлияет на ваш тестовый пример, потому что тесту нужно будет дождаться обработки этого результата разрешенного обещания, прежде чем утверждать его, вы можете использовать jest.runAllTicks (), чтобы гарантировать такое поведение.
Давайте посмотрим, в тесте вы можете удалить async, и особенно await для shallow и update, вам это не нужно, потому что все синхронно. В дополнение к этому, тесту не нужно следить за componentDidMount, потому что он тестируется неявно, если состояние изменилось из-за кода внутри него, это означает, что он был вызван правильно, это лишь незначительное улучшение. Попробуйте записать шаги, которые выполняет функция async, чтобы мы могли обсудить.
Если runAllTicks() не работает, попробуйте это решение, которое часто используется: stackoverflow.com/questions/50454116/…
Пока прогон All Ticks у меня не работал. Объяснение действительно помогло мне понять поток кода, спасибо.
Я столкнулся с той же проблемой. Проблема в том, что тест не дожидается выполнения обещаний. Мое решение состояло в том, чтобы использовать обратный вызов done, предоставленный Jest, чтобы сигнализировать о завершении теста.
Как это:
it('wait async code before assert something', (doneCallback) => {
const wrapper = shallow(<Component />);
setImmediate(() => {
expect(wrapper.find('.async').length).toBe(1);
doneCallback();
});
});
энзим-асинхронные помощники мне очень помог с такого рода проблемами.
Вы можете легко заставить его работать, добавив состояние 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 })
Это отлично сработало, спасибо!
Эта проблема сохраняется, если есть какой-либо асинхронный вызов внутри componentDidMount. Есть ли способ специально подключиться к componentDidMount и дождаться этого? (js.runAllTicks () не помогло)