Фермент: Как проверить изменение реквизита компонента контейнера?

#reactjs #unit-testing #redux #jestjs #enzyme

Вопрос:

Чтобы визуализировать компонент контейнера, подключенный к хранилищу redux, я использую эту функцию для визуализации компонента

 function render(Component, props, storeData) {
  const mockStore = configureStore([thunkMiddleware]);
  const store = mockStore(storeData);

  return mount(
    <Provider store={store}>
      <Component {...props} />
    </Provider>
  );
}
 

Теперь мне нужно проверить изменение реквизитов отображаемого компонента, но, похоже ReactWrapper.setProps , это работает только для корневого компонента.
MyComponent это компонент контейнера, который подключен к хранилищу с помощью connect .

 describe('MyComponent', () => {
  it('should work at props change', () => {
    const wrapper = render(MyComponent, { value: 1 }, initialStoreValue);

    wrapper.find(MyComponent).setProps({ value: 2});
    
    // then expect something.
  });
});
 

Комментарии:

1. Аналогичная проблема возникает, когда я использую компонент, использующий Радий. Мне пришлось обернуть компонент, <StyleRoot> чтобы смонтировать этот компонент, и из-за этого я не смог обновить реквизиты.

Ответ №1:

Несколько вещей, которые следует учитывать:

  1. redux-mock-store позволяет передавать функцию в качестве источника состояния — redux-mock-store будет вызывать эту функцию каждый раз
  2. для запуска компонента повторного рендеринга с useSelector помощью или connect() мы можем просто dispatch() использовать любой тип действия. Буквально любой.
  3. Нам нужно завернуть магазин обновлений в act() , иначе он может не работать и, безусловно, будет жаловаться console.error .

Поэтому, помня об этом, мы можем улучшить render :

 function render(Component, props, initialStoreData) {
  let currentStoreData = initialStoreData;
  const mockStore = configureStore([thunkMiddleware]);
  const store = mockStore(() => currentStoreData);

  const wrapper = mount(
    <Provider store={store}>
      <Component {...props} />
    </Provider>
  );
  const updateStore = (newStoreData) => {
    currentStoreData = newStoreData;
    act(() => {
      store.dispatch({ type: '' }); // just to trigger re-rendering components
    });
  }
  return { wrapper, updateStore };
}
 

А затем в тесте:

 describe('MyComponent', () => {
  it('does something when store changes somehow', () => {
    const { wrapper, updateStore } = render(MyComponent, { value: 1 }, initialStoreValue);
    
    updateStore({ someReduxValue: 2});

    // then expect something.
  });
});
 

Комментарии:

1. Но это не обновляет реквизиты, предоставленные компоненту, не так ли?

2. Я не понимаю вопроса. Если тестируемый компонент на самом деле connect() базовый компонент, то базовый компонент получит новые реквизиты после обновления хранилища. И все внутри, что потребляет реквизит, будет другим(что вам на самом деле лучше проверить вместо обнаружения изменений реквизита — скрывается ли кнопка, изменяется ли текстовая метка и т. Д.)

3. или вы имеете в виду, что при таком подходе реальные редукторы не включаются и запуск действия не обновляет хранилище соответствующим образом? но в том-то и дело: вы просто издеваетесь над данными хранилища вместо того, чтобы издеваться над каждым отдельным fetch или другими сторонними вызовами API.