Коли шкідливо тестувати ваші компоненти

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);

Тепер нам не потрібно спеціальних інструментів і рендеринга компонент. Міняйте компоненти скільки завгодно, тести залишаться актуальними та допоможуть вам при кожному коммите.

Якщо сильно хочеться, то можна тестувати і сам компонент. Але що це дає? Питання відкрите.

Напрошується висновок: Складність написання тестів – ознака поганого дизайну, привід для того, щоб ще раз подумати над архітектурою коду. Замість того, щоб вивертатися і писати непідтримувані тести з рендерингом у псевдобраузере, простіше навести порядок і писати прості зрозумілі юніт тести, які додатково документують ваш додаток.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.