Моя уніфікована теорія вад

Пропоную вам переклад статті одного з інженерів Google, який спеціалізується на автоматизації тестування програмного забезпечення.

Я гадаю, що вади можна поділити на три основні категорії.

* Логічні. Логічні вади є основними, і найчастішими. Це ваші if'и, цикли та інша логіка в коді. Вони на сьогоднішній день є найбільш поширеним видом помилок у програмному забезпеченні. (Думка: це є неправильно).

* Вади взаємодії. Вади взаємодії — це коли два різних об'єкти не правильно взаємодіють один з одним. Наприклад, вивід імені у полі «прізвище». Також яскравим прикладом є ситуація, коли один об'єкт дає на вихід не те, чого від нього очікує інший.

* Вади відображення. Вади відображення — це коли вивід (зазвичай, якийсь ГК або репорт) відображається некоректно. Ключовий момент — у тому, що правильність і неправильність відображення визначає людина. (Думка: вигладає неправильно).

ЗАУВАЖЕННЯ: Деякі розробки гадають, що з часів, як вони почали використовувати графічний користувацький інтерфейс, усі вади стали вадами відображення! Під вадами відображення розуміються помилки, на кшталт, виходу тексту на кнопці за її межі. Якщо ж ви натискаєте на кнопку, і відбувається щось неправильне — це швидше за все, вада взаємодії або ж логічна вада. Вади відображення є досить рідкими.

Типовий розподіл видів вад у програмному забезпеченні (без урахування стабільності)

Перша річ, яку я зауважив про три типами вад — це те, що ймовірність зустріти кожен з них нерівномірна. Окрім ймовірності різними є ще й складність їх пошуку та виправлення в коді (я упевнений, що ви так само знаєте це зі свого досвіду). Мій досвід побудови веб-додатків каже про те, що найбільш поширеними є логічні вади, за ними йдуть вади взаємодії, а вже тоді — вади відображення

.

Вартість виявлення вад

Логічні вади виявити складніше всього. Одна з причин полягає у тому, що вони проявляються лише за певних вхідних умовах, і пошук цих загадкових наборів або їх відтворення можливий лише шляхом перебору великої кількості варіантів. Вади взаємодії простіше виявити, оскільки вони легко відтворються на більшості незалежних вхідних умов. А вади відображення ви можете без будь яких перешкод побачити власними очима і швидко побачити, що виглядає не так.

Складність виправлення вад

Практичний досвід може показати нам, наскільки складно виправляти помилки. Логічні вади виправити досить складно, оскільки для пошуку рішення ви повинні чітко розуміти усі можливі шляхи виконання коду, перед, як здогадаєтесь, що може бути не так, і зможете створити рішення для виправлення помилки. Після внесення правок, ми так само повинні бути впевнені у тому, що зміни не зламали вже існуючу функціональність. Проблеми взаємодії виправити трохи простіше, оскільки вони виявляють себе за допомогою exсeption'ів або невірного місцезнаходженням даних. Вади відображення самі наочно показують, що пішло не так, і ви одразу ж знаєте, як їх виправити. Ми спочатку проектуємо нашу програму, з огляду на часті зміни користувальницького інтерфейсу, і тому нам легше вносити подібні правки.

Логічні зв'язування відображення

Ймовірність появи Висока Середня Низька

Складність виявлення Складно Легко тривіально

Складність виправлення Висока Середня Низька

Як тестопрігодность змінює розподіл видів багів?

Тож, виходить, що написання піддатного до тестування коду впливає на розподіл видів вад у програмі. Піддатний до тестування код повинен:

* Мати явне розділення відповідальності класів. Це зменшує ймовірність появи вад взаємодії. До того ж, менша кількість коду на клас призводить і до меншої кількості логічних вад.

* Використовувати ін'єкції залежностей (dependency injection) — це робить взаємодію явною, на відміну від одинаків (singletons), глобальних об'єктів або локатора сервісів.

* Утримувати явне розділення логіки та взаємодії — взаємодію легше тестувати, якщо у ній не намішано логіки.

Дотримання цих правил веде до істотного скорочення вад взаємодії. У відсотковому відношенні кількість логічних вад виростає, однак, загальна кількість усіх вад скорочується.

Цікаво відзначити, що можна отримати користь від тестабельного коду, не написавши при цьому ні рядка самих тестів. Подібний код — кращий код! (Коли я чую, як люди приносять в жертву «хороший код» ради тестопрігодності, то розумію, що вони зовсім не усвідомлюють, що таке насправді тестопрігодний код)

Ми любимо писати модульні тести (unit tests)

Модульні тести є потужною зброєю. Дані тести призначені для виявлення найбільш поширених вад, складних для виявлення та виправлення. Модульні тести також сприяють написанню тестабельного коду, що неявно допомагає з вадами взаємодії. У результаті, при написанні автоматичних тестів ми повинні більше зосередитися на модульних тестах. Кожен подібний тест фокусується тільки на логіці одного класу/методу.

* Модульні тести сфокусовані на логічних вадах. Вони перевіряють ваші if'и та цикли, але не перевіряють взаємодію прямо (і звичайно, не перевіряють відображення)

* Модульні тести сфокусовані на КПТ (клас-під-тестом, CUT, class-under-test). Це важливо, оскільки ви повинні бути впевнені у тому, що ці тести не встануть на шляху майбутнього рефакторингу. Модульні тести повинні допомагати рефакторингу, а не заважати йому. (Повторюсь: коли я чую, як хтось говорить, що тести заважають рефакторингу, то вважаю, що ця людина не розуміє, що таке модульний тест).

* Модульні тести прямо не скажуть, що все OK із взвємодією. Вони роблять це неявно, шляхом примусу вас до написання тестабельного коду.

* Функціональні тести перевіряють взаємодію, однак це не все. Ви можете довго робити рефакторинг, якщо у вас багато функціональних тестів АБО якщо ви змішує функціональні і логічні тести.

Управління вашими вадами

Я люблю думати про тестування, як про управління вадами (з метою їх позбутись). Не всі види помилок однакові, тому я обираю тести, на яких потрібно сконцентруватися. Я зрозумів, що люблю модульні тести. Але вони повинні бути добре сфокусовані! Якщо тест перевіряє багато класів за один прохід, то я можу бути задоволений хорошим покриттю тестами, але насправді після цього складно знайти те місце, яке запалило «червоний» тест. Це також може ускладнити рефакторинг. Я намагаюся полегшити собі життя при функціональному тестуванні: для мене достатньо одного тесту, який доводить правильність взаємодії.

Я чую, як багато хто заявляє про те, що вони пишуть модульні тести, але при найближчому розгляді це виявляється сумішшю функціональних (перевірка взаємодії) та модульних (логіка) тестів. Це відбувається, коли тести пишуться після того, як написаний код, і через це те, що ваш код не є тестабельним. Не тестабельний код веде до появи мокерів (від англ. «Mock» — заглушка) — це тест, для якого необхідно багато заглушок, а ці заглушки, у свою чергу, використовують інші заглушки. Використовуючи тести з мокерамі, ви будете мало у чому впевненими. Подібні тести працюють на дуже високому рівні абстракції, потрібному для того, щоб щось стверджувати на рівні методів. Ці тести глибоко зав'язані на реалізації (це видно за наявності великої кількості зв'язків між заглушками), внаслідок чого будь-який рефакторинг буде дуже болючим.

Оригінал: My Unified Theory of Bugs

© 2009 - 2019, Розробка - соціальна ІТ спільнота.
Контакти: info@rozrobka.com
Правила користування