Тестирование React Components setState с перегрузкой, которая принимает функцию - PullRequest
0 голосов
/ 24 января 2019

Я пытаюсь протестировать компонент 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, этот код завершается ошибкой при повторном вызове, поскольку (я предполагаю) реагирует на пакетные обновления и состояниеэта версия использует устаревшие.

Ответы [ 2 ]

0 голосов
/ 24 января 2019

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

import React from 'react'
import { mount } from 'enzyme'
import CounterComponent from './CounterComponent'

it("Should increase state by one", () => {
  const component = mount(<CounterComponent />)
  const counter = 10;
  component.setState({ counter });
  component.instance().updateCounter();
  expect(component.state().counter).toEqual(counter + 1);
});
0 голосов
/ 24 января 2019

Я придумала решение для этого после некоторых дальнейших размышлений. Я не уверен, что это лучшее решение, но, учитывая, что метод updateCounter в приведенном выше примере передает функцию в вызов setState, я могу просто получить ссылку на эту функцию, выполнить ее с известным состоянием и проверить возвращаемое значение верное.

Полученный тест выглядит так:

it("Should call setState with the expected parameters", () => {

    let updateStateFunction = null;
    const component = new CounterComponent();
    component.setState = jest.fn((func) => { updateStateFunction = func;});

    component.updateCounter();

    const originalState = { counterValue: 10 };
    const expectedState =  { counterValue: 11};

    expect(component.setState).toHaveBeenCalled();
    expect(updateStateFunction(originalState)).toEqual(expectedState);
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...