Я пытаюсь протестировать компонент React, который использует одну из перегрузок для setState, но не уверен, как правильно подтвердить вызов.Примером компонента может быть:
class CounterComponent extends React.Component {
updateCounter() {
this.setState((state) => {
return {
counterValue: state.counterValue + 1
};
});
}
}
Предполагается, что этот метод будет вызываться асинхронно, поэтому он не может полагаться на текущее состояние без вызова setState (поскольку он может измениться до выполнения setState).Кто-нибудь может подсказать, как бы вы отстаивали этот призыв?Следующий тест не пройден, так как он просто сравнивает имена функций.
it("Should call setState with the expected parameters", () => {
const component = new CounterComponent();
component.setState = jest.fn(() => {});
component.state = { counterValue: 10 };
component.updateCounter();
const anonymous = (state) => {
return {
counterValue: state.counterValue + 1
};
};
//expect(component.setState).toHaveBeenCalledWith({ counterValue: 11 });
expect(component.setState).toHaveBeenCalledWith(anonymous);
});
Редактировать: учитывая ответ yohai ниже, я добавлю некоторый дополнительный контекст, поскольку я чувствую, что, возможно, слишком упростил проблему, однако я не хочупереписать весь вопрос для ясности.
В моем фактическом компоненте редактируемое значение состояния - это не просто число, это массив объектов со структурой:
{ isSaving: false, hasError: false, errorMessage: ''}
и несколько других свойств.Когда пользователь нажимает кнопку «Сохранить», для каждого элемента массива запускается асинхронное действие, а затем обновляется соответствующая запись, когда это действие возвращается или отклоняется.Например, метод сохранения будет выглядеть следующим образом:
onSave() {
const { myItems } = this.state;
myItems.forEach(item => {
api.DoStuff(item)
.then(response => this.handleSuccess(response, item))
.catch(error => this.handleError(error, item));
});
}
Методы обработки и ошибки дескриптора просто обновляют объект и вызывают replaceItem:
handleSuccess(response, item) {
const updated = Object.assign({}, item, { hasSaved: true });
this.replaceItem(updated);
}
handleError(error, item) {
const updated = Object.assign({}, item, { hasError: true });
this.replaceItem(updated);
}
И replaceItem затем заменяет элементв массиве:
replaceItem(updatedItem) {
this.setState((state) => {
const { myItems } = state;
const working = [...myItems];
const itemToReplace = working.find(x => x.id == updatedItem.id);
if (itemToReplace) {
working.splice(working.indexOf(itemToReplace), 1, updatedItem);
};
return {
myItems: working
};
});
}
replaceItem - метод, который я пытаюсь проверить, и пытаюсь проверить, что он вызывает setState с корректной перегрузкой и функцией, которая правильно обновила состояние.
В моем ответе ниже подробно описано, как я решил это для себя, но комментарии и ответы приветствуются =)
@ Vallerii: Тестирование полученного состояния кажется более простым способом, однако, если я это сделаю, то нет никакого способа длятест, чтобы узнать, что метод не делает этого:
replaceItem(updatedItem) {
const { myItems } = state;
const working = [...myItems];
const itemToReplace = working.find(x => x.id == updatedItem.id);
if (itemToReplace) {
working.splice(working.indexOf(itemToReplace), 1, updatedItem);
};
this.setState({ myItems: working });
}
Когда replaceItem не использует правильную перегрузку для setState, этот код завершается ошибкой при повторном вызове, поскольку (я предполагаю) реагирует на пакетные обновления и состояниеэта версия использует устаревшие.