В предыдущем уроке я обсуждал неглубокий или монтируемый в Enzyme. В этом руководстве мы подробнее рассмотрим тестирование с помощью Jest и Enzyme, написав тесты для рендеринга, взаимодействий и вызовов методов жизненного цикла.

Во-первых, у вас должен быть проект под названием «testing-demo-app», который я использовал в предыдущих руководствах. Вы можете найти его по ссылке ниже.



wasuradananjith / testing-demo-app
Это исходный код для серии руководств« Тестирование с помощью Jest и Enzyme в React
github.com»



Давайте напишем несколько тестов для рендеринга, взаимодействий и l вызовов методов ifecycle.

Шаг 1 - Написание тестов для рендеринга

  • Сначала создайте новый файл с именем Form.test .js в папке testing-demo-app / test со следующим содержимым.
import Form from '../src/components/Form';

let wrapper;

beforeEach(() => {
    wrapper = shallow(<Form/>);
});

describe('<Form /> rendering', () => {
    it('should render one <form>', () => {
        expect(wrapper.find('form')).toHaveLength(1);
    });

    it('should not render any <button> when operator is not passed in props', () => {
        expect(wrapper.find('button')).toHaveLength(0);
    });

    it('should render 2 <label>s', () => {
        expect(wrapper.find('label')).toHaveLength(2);
    });

    it('should render 2 <input>s', () => {
        expect(wrapper.find('input')).toHaveLength(2);
    });

});
  • Откройте терминал в каталоге проекта и выполните приведенную ниже команду, чтобы запустить тесты в файле Form.test.js.
npm test Form.test.js
  • Тесты должны пройти следующим образом.

  • Первый тест проверяет, есть ли один элемент ‹form› при отображении ‹Form›.
  • Второй тест особенный. Если вы проверите файл Form.js в testing-demo-app / src / components, вы увидите, что если оператор не передан, будет отображена кнопка. В этом тесте мы это проверяем. Мы не передаем оператор как опору при инициализации оболочки. Из-за этого никакая кнопка не будет отображаться.
  • Третий тест проверяет наличие 2 ‹label› с. (Они должны пометить ПЕРВЫЙ НОМЕР и ВТОРОЙ НОМЕР в пользовательском интерфейсе)
  • Четвертый тест проверяет, есть ли 2 ‹inputs›, в которые нужно ввести добавляемые числа.
  • Давайте добавим еще 2 теста следующим образом и запустим его снова.
it('should render one <button> to Add when operator \'+\' is passed in props', () => {
    wrapper.setProps({ operator: '+' } );
    expect(wrapper.find('#formButtonAdd')).toHaveLength(1);
    expect(wrapper.find('#formButtonSubtract')).toHaveLength(0);
});

it('should render one <button> to Subtract when operator \'-\' is passed in props', () => {
    wrapper.setProps({ operator: '-' } );
    expect(wrapper.find('#formButtonAdd')).toHaveLength(0);
    expect(wrapper.find('#formButtonSubtract')).toHaveLength(1);
});

  • В недавно добавленном пятом тесте мы установили для оператора свойства «+». Согласно тому, что мы обсуждали во время второго теста, теперь ‹Form› должен иметь ‹button›. Здесь мы ищем эту ‹button›, используя ее идентификатор «formButtonAdd».
  • Точно так же в шестом тесте мы устанавливаем для оператора свойства "-" и проверяем, существует ли ‹button› с идентификатором formButtonSubtract. '.

Шаг 2 - Написание тестов для взаимодействий

  • Добавьте еще один набор тестов в «‹Form› взаимодействий» в Form.test.js, как показано ниже, и снова запустите файл.
describe('<Form /> interactions', () => {
    it('should change the state firstNumber when onChange function of the #number1 input is invoked', () => {
        wrapper.find('#number1').simulate('change',
            { target: { value: 50 } }
        );
        expect(wrapper.state('firstNumber')).toEqual(50);
        expect(wrapper.state('secondNumber')).toEqual('');
    });

    it('should change the state secondNumber when onChange function of the #number2 input is invoked', () => {
        wrapper.find('#number2').simulate('change',
            { target: { value: 60 } }
        );
        expect(wrapper.state('secondNumber')).toEqual(60);
        expect(wrapper.state('firstNumber')).toEqual('');
    });
});

  • В первом тесте в разделе «‹Form› взаимодействий» мы моделируем событие в поле ‹input›. Мы моделируем метод onChange в ‹input›, который имеет идентификатор ‘number1’. Мы присваиваем значение number1 ‹input› как 50. Если вы отметите функцию onChange для number1 в Файл Form.js вызывает метод handleFirstNumber (), как показано ниже.
handleFirstNumber(event) {
    this.setState({ firstNumber: event.target.value });
}
  • Итак, согласно нашему тесту, он устанавливает 50 в качестве значения number1 и ожидает, что эта функция автоматически изменит значение state.firstNumber в Form.js до 50. Этот тест пройден, это означает, что мы успешно смоделировали метод onChange для number1.
  • Во втором тесте мы делаем то же самое для имитации метода onChange в number2 ‹input›.
  • Давайте добавим еще 2 теста, как показано ниже, и запустим файл.
it('should call the onClick function when \'Add\' button is clicked when the operator is \'+\'', () => {
    wrapper.setProps({ operator: '+' } );
    const mockedHandleClickAdd = jest.fn();
    wrapper.instance().handleAdd = mockedHandleClickAdd;
    wrapper.find('#formButtonAdd').props().onClick();
    expect(mockedHandleClickAdd).toHaveBeenCalledTimes(1);
});

it('should call the onClick function when \'Subtract\' button is clicked when the operator is \'-\'', () => {
    wrapper.setProps({ operator: '-' } );
    const mockedHandleClickSubtract = jest.fn();
    wrapper.instance().handleSubtract = mockedHandleClickSubtract;
    wrapper.find('#formButtonSubtract').props().onClick();
    expect(mockedHandleClickSubtract).toHaveBeenCalledTimes(1);
});

  • В третьем тесте в разделе «‹Form› взаимодействий» имитируется функция onClick для ‹button› с идентификатором 'formButtonAdd'. . В Form.js при щелчке по нему должна быть вызвана функция с именем handleAdd (). Поскольку мы имитировали это, используя шутливую функцию с именем mockedHandleClickAdd (), вместо нее необходимо вызвать ее. Итак, наш тест прошел. Значит, мы успешно высмеяли функцию onClick в formButtonAdd.
  • Точно так же в четвертом тесте мы имитируем функцию onClick для ‘formButtonSubtract’.

Шаг 3 - Написание тестов для вызовов методов жизненного цикла

  • Добавьте еще один тест в вызовы методов жизненного цикла ‹Form› в Form.test.js, как показано ниже, и снова запустите файл.
describe('<Form /> lifecycle method invocations', () => {
    it('should change the state componentState componentDidMount method is invoked', () => {
        expect(wrapper.state('componentState')).toEqual('mounted');
    });
});

  • Здесь мы проверяем состояние с именем componentState, которое определено в файле Form.js. Согласно Form.js, состояние componentState будет изменено на «смонтировано» при вызове метода componentDidMount. Наш тест пройден, это означает, что метод componentMount вызван успешно.

Полный тестовый файл - Form.test.js

import Form from '../src/components/Form';

let wrapper;

beforeEach(() => {
    wrapper = shallow(<Form/>);
});

describe('<Form /> rendering', () => {
    it('should render one <form>', () => {
        expect(wrapper.find('form')).toHaveLength(1);
    });

    it('should not render any <button> when operator is not passed in props', () => {
        expect(wrapper.find('button')).toHaveLength(0);
    });

    it('should render 2 <label>s', () => {
        expect(wrapper.find('label')).toHaveLength(2);
    });

    it('should render 2 <input>s', () => {
        expect(wrapper.find('input')).toHaveLength(2);
    });

    it('should render one <button> to Add when operator \'+\' is passed in props', () => {
        wrapper.setProps({ operator: '+' } );
        expect(wrapper.find('#formButtonAdd')).toHaveLength(1);
        expect(wrapper.find('#formButtonSubtract')).toHaveLength(0);
    });

    it('should render one <button> to Subtract when operator \'-\' is passed in props', () => {
        wrapper.setProps({ operator: '-' } );
        expect(wrapper.find('#formButtonAdd')).toHaveLength(0);
        expect(wrapper.find('#formButtonSubtract')).toHaveLength(1);
    });
});

describe('<Form /> interactions', () => {
    it('should change the state firstNumber when onChange function of the #number1 input is invoked', () => {
        wrapper.find('#number1').simulate('change',
            { target: { value: 50 } }
        );
        expect(wrapper.state('firstNumber')).toEqual(50);
        expect(wrapper.state('secondNumber')).toEqual('');
    });

    it('should change the state secondNumber when onChange function of the #number2 input is invoked', () => {
        wrapper.find('#number2').simulate('change',
            { target: { value: 60 } }
        );
        expect(wrapper.state('secondNumber')).toEqual(60);
        expect(wrapper.state('firstNumber')).toEqual('');
    });

    it('should call the onClick function when \'Add\' button is clicked when the operator is \'+\'', () => {
        wrapper.setProps({ operator: '+' } );
        const mockedHandleClickAdd = jest.fn();
        wrapper.instance().handleAdd = mockedHandleClickAdd;
        wrapper.find('#formButtonAdd').props().onClick();
        expect(mockedHandleClickAdd).toHaveBeenCalledTimes(1);
    });

    it('should call the onClick function when \'Subtract\' button is clicked when the operator is \'-\'', () => {
        wrapper.setProps({ operator: '-' } );
        const mockedHandleClickSubtract = jest.fn();
        wrapper.instance().handleSubtract = mockedHandleClickSubtract;
        wrapper.find('#formButtonSubtract').props().onClick();
        expect(mockedHandleClickSubtract).toHaveBeenCalledTimes(1);
    });
});

describe('<Form /> lifecycle method invocations', () => {
    it('should change the state componentState componentDidMount method is invoked', () => {
        expect(wrapper.state('componentState')).toEqual('mounted');
    });
});

Специальное примечание о «componentDidMount»

Предположим, у вас есть вызов базы данных в одном из методов жизненного цикла (componentDidMount или любом другом). Если вы вызовете его из тестов, он определенно потерпит неудачу, потому что у вас нет соединения с базой данных во время тестирования. Итак, решение для этого - использовать функцию прототипа componentDidMount, которая определена в вашем тестовом файле, как показано ниже.

beforeAll(() => {
    Form.prototype.componentDidMount = () => {
        console.log('componentDidMount method is called');
    };
});

Заключение

Надеюсь, вы получили четкое представление о написании тестов пользовательского интерфейса для рендеринга, взаимодействия и вызовов методов жизненного цикла с использованием Jest и Enzyme.

Вы можете найти работу, которую мы проделали в этом руководстве, в ветке под названием tutorial-05 в репозитории git ниже.



В следующем руководстве я рассмотрю «Тестирование снимков в Jest». А пока, до свидания!

Предыдущие уроки









Эта история публикуется в Noteworthy, куда ежедневно приходят более 10 000 читателей, чтобы узнать о людях и идеях, формирующих наши любимые продукты.

Подпишитесь на нашу публикацию, чтобы увидеть больше историй о продуктах и ​​дизайне, представленных командой Journal.