Мы завершаем эту серию, исследуя поведение тестируемых компонентов; и намек на тестирование снимков.
Эта статья является частью серии, начинающейся с Руководство для скептиков по тестированию внешнего интерфейса: Часть 1, в которой изучается тестирование внешнего интерфейса путем постепенного создания простого веб-приложения-калькулятора (с использованием React / Redux ).
Примеры из этой серии доступны для скачивания.
Поведение
В последней статье единственным фрагментом открытого кода был обработчик onClick компонента Button; несколько сомнительно отсутствие 100% покрытия кода.
part4 / src / MyApp / components / Calculator / Button / index.jsx
... onClick={() => setFirst(number)} ...
Кроме того, если подумать о компоненте Button, он должен делать больше, чем просто отрисовывать себя; также предполагается, что у него есть механизм для вызова свойства функции setFirst со свойством number. Можно сказать, что это часть контракта на компонент.
Между желанием 100% покрытия кода и тестированием контракта компонента мы внесем некоторые улучшения в тесты компонента Button.
Прежде чем мы перейдем к тестированию поведения, есть ряд вещей, которые мы можем улучшить с помощью нашего компонентного тестирования.
Во-первых, казалось целесообразным заключить наши тесты в блоки с подходящим названием description (а не просто полагаться на имя файла теста), например, для тестирования компонента Calculator:
/part4/src/MyApp/components/Calculator/Calculator.test.jsx
... describe('Calculator component', () => { it('shallow renders without crashing', () => { .. }); });
Кроме того, в нашем последнем примере мы повторили код, который неглубоко отрисовывает компонент; эта проблема была бы еще хуже, если бы у нас было много общих свойств, которые мы передали для каждого теста.
/part3/src/MyApp/components/Calculator/Display/Display.test.jsx
... it('shallow renders without crashing with number', () => { shallow(<Display first={1} />); }); it('shallow renders without crashing with null', () => { shallow(<Display first={null} />); }); ...
Прочитав ряд статей, я нашел особенно элегантное решение, позволяющее избежать повторяющегося кода с помощью фабричной функции, setup, в статье Тестирование в React: лучшие практики, советы и приемы .
part4 / src / MyApp / компоненты / Калькулятор / Дисплей /Display.test.jsx
import React from 'react'; import Enzyme, { shallow } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import { Display } from '../Display'; Enzyme.configure({ adapter: new Adapter() }); const setup = (propOverrides) => { const props = { ...propOverrides, }; return ({ props, wrapper: shallow(<Display {...props} />), }); }; describe('Display component', () => { describe('shallow renders without crashing', () => { it('with first property undefined', () => { setup({}); }); it('with first property number', () => { setup({ first: 1 }); }); it('with first property null', () => { setup({ first: null }); }); }); });
Наблюдения:
- В этом примере не используются возможности общих свойств (в следующем примере это будет).
- В этом примере не используются возвращенные свойства или оболочка (в следующем примере это будет).
Используя тот же шаблон, мы можем реорганизовать тест компонента Button:
part4 / src / MyApp / компоненты / Калькулятор / Кнопка /Button.test.jsx
import React from 'react'; import Enzyme, { shallow } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import { Button } from '../Button'; Enzyme.configure({ adapter: new Adapter() }); const setup = (propOverrides) => { const props = { number: 0, setFirst: jest.fn(), ...propOverrides, }; return ({ props, wrapper: shallow(<Button {...props} />), }); }; describe('Button component', () => { it('shallow renders without crashing', () => { setup({}); }); it('calls setFirst on click', () => { const NUMBER = 1; const { props: { setFirst }, wrapper } = setup({ number: NUMBER }); wrapper.simulate('click'); expect(setFirst.mock.calls).toHaveLength(1); expect(setFirst.mock.calls[0][0]).toBe(NUMBER); }); });
Наблюдения:
- В этом тесте у нас установлены общие свойства number и setFirst; setFirst - это фиктивная функция Jest.
- У нас есть второй тест поведения, который нажимает кнопку и гарантирует, что функция setFirst вызывается один раз с правильным значением (свойство number).
- Если подумать о том, как написано большинство приложений React / Redux, большая часть (если не все) поведения сводится к вызову компонентов, переданных в функциях (создателях действий). Этот тест является представителем этого типа поведенческого тестирования.
- Наконец, с проведением поведенческого теста у нас теперь есть 100% покрытие (кода, который нас интересует в этом примере).
Тестирование снимков
Наконец, вы можете подумать, что нам также следует рассмотреть возможность добавления функции Jest тестирование снимков в нашу стратегию тестирования.
Тесты моментальных снимков - очень полезный инструмент, когда вы хотите убедиться, что ваш пользовательский интерфейс не изменится неожиданно.
Типичный тестовый пример моментального снимка для мобильного приложения отображает компонент пользовательского интерфейса, делает снимок экрана, а затем сравнивает его с сохраненным эталонным изображением. рядом с тестом. Тест завершится неудачно, если два изображения не совпадают: либо изменение является неожиданным, либо снимок экрана необходимо обновить до новой версии компонента пользовательского интерфейса.
- Команда Jest
Возвращаясь к моему беспокойству по поводу тестирования декларативного кода, мне интересно, какова ценность (по крайней мере, в этом примере) моментального тестирования компонентов. Например, если подумать о компоненте Display:
hello-fe-testing / part4 / src / MyApp / компоненты / Калькулятор / Дисплей /index.jsx
import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import * as fromFirst from '../../../ducks/first'; export function Display({ first }) { return ( <div>{first !== null && first}</div> ); } Display.propTypes = { first: PropTypes.number, }; Display.defaultProps = { first: null, }; export default connect( state => ({ first: fromFirst.getFirst(state), }), null, )(Display);
Не уверен, в каком сценарии тест снимка поможет мне свести к минимуму будущие ошибки в этом в высшей степени декларативном коде. По сути, здесь можно было бы что-то изменить, только если бы они намеревались это сделать, и это привело бы к сбою теста.
Подведение итогов
В конце концов, я решил не реализовывать полноценное веб-приложение калькулятора (простой пример, представленный здесь, демонстрирует все стратегии тестирования, которые я считал важными). Общие правила были:
- Проведите интеграционный тест всего приложения.
- Проведите неглубокий дымовой тест всех компонентов (неподключенных).
- Протестируйте все редукторы Redux.
- Протестируйте все поведения в контракте компонента (должно быть все поведение с приложением React / Redux).
В то же время кажется, что тестирование - это тема, по которой существует множество мнений; хотел бы услышать ваше.