Когда вредно тестировать ваши компоненты

от автора

image
Автоматизированные тесты – это хорошо. Проект, который на 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *