Автоматизированные тесты – это хорошо. Проект, который на 100% покрыт тестами, преподносит покрытие как преимущество. Но…
Думаю, что в этом процессе нет осознанности. А она сильно облегчает жизнь. Возможно, что половина тестов в вашем проекте не только бесполезна, более того — несет вред. Ниже расскажу о том, какие тесты писать не нужно.
Рассмотрим компонент на ReactJS:
class FilterForm extends React.Component { constructor(props) { super(props); this.state = { value: '', frameworks: ['react', 'angular', 'ember', 'backbone'] }; this.handleChange = this.handleChange.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } render() { const filteredElements = this.state.frameworks .filter(e => e.includes(this.state.value)) .map(e => <li>{ e }</li>) return ( <div> <input type="text" value={this.state.value} onChange={this.handleChange} /> <ul>{ filteredElements }</ul> </div> ); } }
Тесты для этого возможно выглядят как-то так:
test('should work', () => { const wrapper = shallow(<FilterForm />); expect(wrapper.find('li').length).to.equal(4); wrapper.find('input').simulate('change', {target: {value: 'react'}}); expect(wrapper.find('li').length).to.equal(1); });
Давайте подумаем, что этот код тестирует. Это важно. А тестирует он по большому счету эту строку:
.filter(e => e.includes(this.state.value))
Конечно, мы тестируем еще и то, как ReactJS рендерит изменения и как навешены события на DOM, как они обрабатываются и прочее. Но именно эта строка здесь явно главная.
А так ли нам нужно тестировать весь компонент? При этом подходе к написанию кода мы не можем иначе. У нас нет выбора.
В конкретном примере это не вызывает проблем, но почти всегда компоненты с течением времени становятся сильно сложнее. Логика и DOM компоненты перемещаются из одного компонента в другой.
Например, следующим шагом имеет смысл разбить один компонент на два: форма ввода и лист. Каждый элемент листа тоже имеет смысл сделать отдельным компонентом.
После таких изменений придется менять тесты. Вернее не менять, а выкидывать и писать заново. Они станут бесполезными, так как завязаны на DOM. Возникает вопрос: «А зачем такие тесты нужны!?»
Тесты, которые нужно менять после каждого изменения в коде, бесполезны и даже вредны, поскольку требуют затрат на сопровождение и не приносят никакой пользы.
Конечно, можно использовать некую абстракцию, типа PageObject (eng), но проблема не будет решена полностью.
Дело в том, что сам компонент написан плохо. Код для фильтрации нельзя использовать повторно. Если нам понадобится фильтрация в другом компоненте, то при таком подходе придется писать и тестировать все заново.Нарушен принцип единственности ответственности.
Помимо логики для вывода в компоненте есть еще и логика для фильтрации. Решение есть, и оно очень простое. Можно перенести код, который отвечает за фильтрацию в другое место и тестировать там.
export function filter(list, value){ return list.filter(e => e.includes(value)) } expect(filter(list, ‘react’).length).to.equal(1);
Теперь нам не нужно специальных инструментов и рендеринга компонент. Меняйте компоненты сколько угодно, тесты останутся актуальными и помогут вам при каждом коммите.
Если сильно хочется, то можно тестировать и сам компонент. Но что это дает? Вопрос открытый.
Напрашивается вывод: Сложность написания тестов – признак плохого дизайна, повод для того, чтобы еще раз подумать над архитектурой кода. Вместо того, чтобы изворачиваться и писать неподдерживаемые тесты с рендерингом в псевдобраузере, проще навести порядок и писать простые понятные юнит тесты, которые дополнительно документируют ваше приложение.
ссылка на оригинал статьи https://habrahabr.ru/post/315908/
Добавить комментарий