React + Jest - Тестирование асинхронных компонентов и ожидание монтирования

Я пытаюсь протестировать компонент 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
}
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
0
6 209
3

Ответы 3

Странное поведение, вероятно, связано с тем, что вы использовали async для реализации componentDidMount.

В настоящее время весь процесс рендеринга React является синхронным, поэтому все, что связано с его потоком рендеринга, следует рассматривать как синхронное. И случается, что в настоящее время команда React также разрабатывает функцию критических изменений, чтобы разрешить асинхронный рендеринг.

Но! Даже после того, как эта функция станет доступной, имейте в виду, что componentDidMountкрючок жизненного цикла по-прежнему будет синхронным, и все остальные перехватчики тоже, поэтому важно знать, что React не будет ждать разрешения какого-либо обещания внутри перехватчиков.

Вы можете запустить Promise внутри componentDidMount, если он соответствует вашему варианту использования, и позволить разрешенному результату изменить состояние. Но крючок жизненного цикла завершится до того, как он разрешится, это повлияет на ваш тестовый пример, потому что тесту нужно будет дождаться обработки этого результата разрешенного обещания, прежде чем утверждать его, вы можете использовать jest.runAllTicks (), чтобы гарантировать такое поведение.

Эта проблема сохраняется, если есть какой-либо асинхронный вызов внутри componentDidMount. Есть ли способ специально подключиться к componentDidMount и дождаться этого? (js.runAllTicks () не помогло)

casraf 06.06.2018 10:01

Давайте посмотрим, в тесте вы можете удалить async, и особенно await для shallow и update, вам это не нужно, потому что все синхронно. В дополнение к этому, тесту не нужно следить за componentDidMount, потому что он тестируется неявно, если состояние изменилось из-за кода внутри него, это означает, что он был вызван правильно, это лишь незначительное улучшение. Попробуйте записать шаги, которые выполняет функция async, чтобы мы могли обсудить.

rodgobbi 06.06.2018 15:49

Если runAllTicks() не работает, попробуйте это решение, которое часто используется: stackoverflow.com/questions/50454116/…

rodgobbi 06.06.2018 15:50

Пока прогон All Ticks у меня не работал. Объяснение действительно помогло мне понять поток кода, спасибо.

Dale C 27.01.2021 23:34

Я столкнулся с той же проблемой. Проблема в том, что тест не дожидается выполнения обещаний. Мое решение состояло в том, чтобы использовать обратный вызов 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 })

Это отлично сработало, спасибо!

Dale C 27.01.2021 23:35

Другие вопросы по теме