Значение setState, используемое в componentDidMount, не отражается в тесте Enzyme.

Component.js

import React from 'react'
import request from 'superagent'

export default React.createClass({
    getInitialState() {
        return {cats: []}
    },

    componentDidMount() {
        request('/api', (err, res) => {
            if (err) return;
            this.setState({
                cats: res.body.results
            })
        })
    },

    render() {
        let cats = this.state.cats
        let catsList = (
            <ul>
                {cats.map((c) => <li key={c.id}>cat</li>)}
            </ul>
        )
        return (
            <div>
                {cats.length ? catsList : null}
            </div>
        )
    }
})

Компонент.test.js

jest.unmock('../app.js')
jest.unmock('superagent')

import React from 'react'
import {mount} from 'enzyme'
import nock from 'nock'
import App from '../app.js'

describe('App', () => {
    let ajaxFn
    beforeEach(() => {
        ajaxFn = nock('http://localhost')
            .get('/api')
            .reply(200, {
                results: [{id: 1}, {id: 2}, {id: 3}]
            })
    })

    it('renders correct number of cats', () => {
        let wrapper = mount(<App />)
        expect(wrapper.state('cats').length).toBe(3)
    })
})

Тест не проходит. wrapper.state('cats').length всегда 0.

Я понимаю, что setState не гарантирует немедленное обновление состояния, однако, если я зарегистрирую «кошки» в компоненте, я увижу его обновление.


person Babistalikesflyingonrails    schedule 10.03.2016    source источник


Ответы (3)


Если вы в конечном итоге установите состояние в своем компоненте в каком-то контексте, о котором фермент не знает, вам придется вручную вызвать .update() в оболочке, чтобы он получил обновленную версию дерева рендеринга.

it('renders correct number of cats', () => {
    let wrapper = mount(<App />)
    expect(wrapper.update().state('cats').length).toBe(3)
})
person Leland Richardson    schedule 11.03.2016
comment
Это сработало для меня, однако я задавался вопросом, почему он не обновляется в результате setState, пересекает ли оболочка копию дерева рендеринга или что-то в этом роде? - person riscarrott; 12.05.2016
comment
Лиланд, разве вам не нужно сначала убедиться, что обновление состояния произошло, прежде чем делать wrapper.update() ? - person bodrin; 29.11.2016
comment
или мы полагаемся на тот факт, что ajaxFn с отключенным кодом синхронизируется - person bodrin; 29.11.2016
comment
Я разместил ответ здесь stackoverflow .com/questions/38308214/ .. это может быть похоже на ответ Саймона Хардмана - person bodrin; 29.11.2016

У меня была аналогичная проблема и нужно было вернуть промис из колбэка it и проверить ожидание в методе then промиса.

В вашем случае (при условии, что ajaxFn был обещанием, или вы могли бы превратить его в одно), я думаю, что это будет примерно:

it('renders correct number of cats', () => {
    let wrapper = mount(<App />) 
    return ajaxFn.then(() => {
        expect(wrapper.state('cats').length).toBe(3);
    }
})
person Simon Hardman    schedule 03.11.2016

Я не знаком со всеми библиотеками, которые вы используете, но поскольку ваш код выполняется асинхронно, тест завершается до того, как состояние может быть обновлено. Я смог решить эту проблему, добавив в тест async/await:

it('renders correct number of cats', async () => {
    let wrapper = await mount(<App />)
    expect(wrapper.state('cats').length).toBe(3)
})
person David    schedule 17.06.2018
comment
Спасибо, сэр, я не знал, что mount — это асинхронная функция! - person anni; 19.11.2018