Фермент: при мелком обновлении редукционного магазина не запускается componentDidUpdate - PullRequest
0 голосов
/ 13 ноября 2018

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

Я полагаю, что проблема заключается в том, что энзимный shallow метод не вызывает componentDidUpdate в моем компоненте. Я воспроизвел проблему с помощью самого простого кода, который только мог, и подтвердил, что проблема исчезнет, ​​если я использую mount вместо shallow. Однако документация фермента говорит, что начиная с версии 3, shallow вызывает методы жизненного цикла React, такие как componentDidUpdate. И, конечно, я не могу использовать mount в своем тесте, потому что я не хочу монтировать все дерево (в моем случае это означало бы загрузку всего моего приложения).

Таким образом, я задаюсь вопросом: это ошибка в ферменте, или я что-то упускаю при использовании этой библиотеки?

Вот мой пример кода:

import * as React from 'react';
import { connect } from 'react-redux';
import { createStore } from 'redux';

import { shallow } from 'enzyme';
import sinon from 'sinon';


const actions = {
    // This would fetch a resource on the network.
    getList: () => {},

    receiveList: list => {
        return {
            type: 'RECEIVE_LIST',
            list,
        };
    },

    // This would fetch a list of resources on the network.
    getObjects: () => {},
};


const reducer = (state = { list: [] }, action) => {
    switch (action.type) {
        case 'RECEIVE_LIST':
            return { ...state, list: action.list };
        default:
            return state;
    }
};


class MyAppBase extends React.Component {
    componentDidMount() {
        this.props.dispatch(actions.getList());
    }

    componentDidUpdate(nextProps) {
        if (nextProps.list !== this.props.list) {
            this.props.dispatch(actions.getObjects(nextProps.list));
        }
    }

    render() {
        return null;
    }
}


const MyApp = connect(state => { return { list: state.list }; })(MyAppBase);



describe('<MyApp>', () => {
    it('fetches objects once list is received', () => {
        // Mock actions that call the network.
        sinon.stub(actions, 'getList');
        actions.getList.returns({type: 'whatever'});
        sinon.stub(actions, 'getObjects');
        actions.getObjects.returns({type: 'whatever'});

        // Create a redux store.
        const store = createStore(reducer);

        // Create the component.
        const wrapper = shallow(<MyApp store={store} />);

        // Verify that `componentDidMount` was called but not `componentDidUpdate`.
        expect(actions.getList.callCount).toEqual(1);
        expect(actions.getObjects.callCount).toEqual(0);

        // Dispatch an action that will change the redux store.
        store.dispatch(actions.receiveList([ 'aa', 'bb' ]));

        // Expect the `componentDidUpdate` method to be called.
        expect(actions.getObjects.callCount).toEqual(1);

        // This doesn't work, for some reason `componentDidUpdate` is never called.
    });
});

Версии пакета, которые я использую для этого:

"react": "16.6.0",
"react-dom": "16.6.0",
"react-redux": "5.1.0",
"redux": "4.0.1",
"enzyme": "3.7.0",
"enzyme-adapter-react-16": "1.6.0",
"sinon": "7.1.0"

Спасибо!

1 Ответ

0 голосов
/ 26 февраля 2019

Метод "componentDidUpdate", по-видимому, не полностью поддерживается "поверхностным" методом энзима.

Компонент, установленный shallow, похоже, вызывает только componentDidUpdate после вызова wrapper.setProps(). Неважно, что магазин обновляется с новым состоянием, оболочка просто не обновляется.

Это можно подтвердить, изменив тест на следующее:

// Dispatch an action that will change the redux store.
store.dispatch(actions.receiveList([ 'aa', 'bb' ]));

// Will print '[]'
console.log(wrapper.instance().props.list);

// Create a new wrapper with the current store:
const wrapper2 = shallow(<MyApp store={store} />);

// Will print '['aa', 'bb']'
console.log(wrapper2.instance().props.list)

В качестве обходного пути можно было бы позвонить setProps после store.dispatch (или, желательно, просто использовать mount вместо shallow, если это возможно):

// Dispatch an action that will change the redux store.
store.dispatch(actions.receiveList([ 'aa', 'bb' ]));

// Manually cause componentDidUpdate to trigger
wrapper.setProps({
  list: ['aa', 'bb']
});

Кроме того, componentDidUpdate может быть вызвано методом simulate для щелчка или изменения кнопки или ввода.

...