Як я можу пропонувати тестування одиничного коду на приватному коді?


15

Я намагаюсь відстоювати тестування одиниць у своїй робочій групі, але заперечення, які я часто отримую, - це те, що його слід використовувати лише для зовнішнього експорту API (що є лише мінімальною та не критичною частиною нашої системи), а не для внутрішніх та приватних код (який тепер має лише функціональне тестування).

Хоча я вважаю, що тест одиниці можна і потрібно застосовувати до всього коду, як я можу переконати своїх колег?


3
Якщо у вас є приватні методи, які ви відчуваєте необхідність тестувати, це часто є ознакою того, що ваш код порушує SRP, і там є ще один клас, який плаче витягнути і випробувати його самостійно.
Paddyslacker

@Paddyslacker: Я вважаю, що весь код потрібно перевірити. Я не розумію, чому блок коду, який дотримується принципу єдиної відповідальності, не слід піддавати тестуванню підрозділу ...
Wizard79

4
@lorenzo, ти пропустив мій погляд; можливо, я не дуже вдався. Якщо ви вилучите ці приватні методи в інший клас, тепер вони повинні бути доступними з вашого початкового класу. Оскільки методи тепер є загальнодоступними, їх потрібно буде перевірити. Я не мав на увазі, що їх не слід перевіряти, я мав на увазі, що якщо ви відчуваєте необхідність безпосередньо протестувати методи, цілком ймовірно, що вони не повинні бути приватними.
Paddyslacker

@Paddyslacker: Я відчуваю потребу безпосередньо перевірити також приватні методи. Чому ви вважаєте, що вони не повинні бути приватними?
Wizard79

6
Тестуючи приватні методи, ви порушуєте абстракцію. Ви повинні перевіряти стан та / або поведінку, а не реалізацію, в одиничних тестах. Ваші приклади / сценарії повинні бути в змозі перевірити, що досягає результат приватного коду - якщо вам це складно, то, як каже Paddyslacker, це може означати, що ви порушуєте SRP. Це також може означати, що ви не перегонили свої приклади, щоб бути справді представником того, що робить ваш код.
FinnNk

Відповіді:


9

Ваші колеги можуть плутати справжні тестові одиниці з тестами інтеграції. Якщо ваш продукт є (або має) API, тести інтеграції можуть бути запрограмовані як тестові приклади NUnit. Деякі люди помилково вважають, що це одиничні тести.

Ви можете спробувати переконати своїх колег у наступному (я впевнений, що ви вже знаєте цей матеріал, все, що я говорю, це те, що вказати його вашим колегам може допомогти):

  • Тестове покриття . Виміряйте фактичний відсоток покриття тесту цих інтеграційних тестів. Це перевірка реальності для тих, хто ніколи не працював на тестовому покритті. Оскільки важко виконувати всі логічні шляхи, коли вхід знаходиться в декількох шарах, тестове покриття перевищує десь 20% до 50%. Щоб отримати більш широке охоплення, ваші колеги повинні написати справжні, ізольовані одиничні тести.
  • Конфігурація . Розгорніть те саме програмне забезпечення, яке випробовуєте, і, можливо, ви зможете продемонструвати своїм колегам, як важко запускати свої тести в інших умовах. Шляхи до різних файлів, рядки підключення до БД, URL-адреси віддалених служб тощо - все це складає.
  • Час виконання . Якщо тести не є справжніми тестовими одиницями і не можуть працювати в пам’яті, для їх виконання знадобиться багато часу.

12

Причини використання тестування одиниць у внутрішньому / приватному коді точно такі ж, як і для API, що підтримуються зовні:

  • Вони запобігають повторенню помилок (одиничні тести є частиною набору тестів на регресію).
  • Вони документують (у виконуваному форматі!), Що код працює.
  • Вони дають виконавче визначення того, що означає "код працює".
  • Вони надають автоматизований засіб демонстрації того, що код дійсно відповідає специфікаціям (як визначено вище).
  • Вони показують, як модуль / клас / модуль / функція / метод виходить з ладу за наявності несподіваного вводу.
  • Вони надають приклади, як користуватися підрозділом, що є чудовою документацією для нових членів команди.

8

Якщо ви маєте на увазі приватне так, як я думаю, ви це маєте на увазі, то ні - вам не слід тестувати його. Вам слід проводити лише тестування на стан поведінки / стану, що спостерігається Можливо, вам не вистачає точки за циклом TDD "червоно-зелений-рефактор" (і якщо ви не робите тест спочатку, застосовується той же принцип). Після тестування та здачі тестів ви не хочете, щоб вони змінювалися під час виконання рефакторингу. Якщо ви змушені використовувати тест приватних функціональних можливостей, це, ймовірно, означає, що одиничні тести навколо загальнодоступних функціональних можливостей є помилковими. Якщо складно і складно написати тести навколо публічного коду, можливо, ваш клас робить занадто багато, або ваша проблема не чітко визначена.

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

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

Ось трохи дурного прикладу, звичайно, у реальному житті у вас було б більше тестів, ніж у цих:

Скажімо, у вас є клас, який повертає відсортований список рядків - ви повинні перевірити, чи результат сортований, а не як він насправді сортує цей список. Ви можете розпочати реалізацію за допомогою одного алгоритму, який просто сортує список. Після того, як це зроблено, ваш тест не потрібно змінювати, якщо ви зміните алгоритм сортування. На даний момент у вас є єдиний тест (якщо припустити, що сортування вбудовано у ваш клас):

  1. Чи сортується мій результат?

Тепер скажіть, що ви хочете два алгоритми (можливо, один є ефективнішим за деяких обставин, але не для інших), тоді кожен алгоритм міг (і взагалі повинен бути) наданий іншим класом, і ваш клас вибирає з них - ви можете перевірити, що це відбувається для обрані вами сценарії з використанням макетів, але ваш оригінальний тест все ще діє, і оскільки ми лише перевіряємо поведінку / стан, що спостерігається, його не потрібно змінювати. Ви закінчуєте 3 тести:

  1. Чи сортується мій результат?
  2. Враховуючи сценарій (скажімо, початковий список майже відсортований для початку) - це виклик до класу, який сортує рядки за допомогою алгоритму X?
  3. Враховуючи сценарій (початковий список знаходиться у випадковому порядку) - це виклик до класу, який сортує рядки за допомогою алгоритму Y?

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

Тести (такого типу) повинні змінюватися лише тоді, коли змінюється поведінка, починати писати тести проти приватного коду, і це виходить у вікно.


1
Можливо, є непорозуміння щодо значення "приватного". У нашій системі 99% коду є "приватним", тоді у нас є невеликий API для автоматизації / дистанційного керування одним із компонентів системи. Я маю на увазі одиничне тестування коду всіх інших модулів.
Wizard79

4

ось ще одна причина: у гіпотетичному випадку мені доведеться вибирати між тестуванням підрозділу зовнішнього API та приватним частинами, я б обрав приватні частини.

Якщо кожна приватна частина покрита тестом, API, що складається з цих приватних частин, також повинен бути покритий майже на 100%, за винятком лише верхнього шару. Але це, ймовірно, тонкий шар.

З іншого боку, лише при тестуванні API може бути дуже важко повністю покрити всі можливі кодові шляхи.


+1 "з іншого боку ..." Але якщо нічого іншого, додайте тести, де збій зашкодив би найбільше.
Тоні Енніс

2

Важко змусити людей приймати тестування на одиницю, тому що це здається марною витратою часу ("ми могли б кодувати ще один проект з отримання грошей!") Або рекурсивно ("І тоді ми повинні писати тестові справи для тестових випадків!") Я винен сказати і те, і інше.

Перший раз, коли ви виявите помилку, вам доведеться зіткнутися з правдою, що ви не ідеальні (як швидко ми забули програмісти!), І ви переходите до "Хммм"


Ще одним аспектом тестування одиниць є те, що код повинен бути записаний, щоб бути перевіреним. Зрозумівши, що деякий код легко перевірити, а якийсь код не робить хорошого програміста "Hmmm".


Ви запитували свого колегу, чому тестування одиниць корисно лише для зовнішніх API?


Один із способів показати значення тестування одиниць - дочекатися появи неприємної помилки, а потім показати, як тестування одиниці могло це запобігти. Це не втирати їх обличчям, це, щоб, по їх думках, перенести тестування одиниць із Теоретичної вежі із Слонової кістки на реальність.

Ще один спосіб - чекати, поки однакова помилка трапиться двічі . "А-а-ну, бос, ми додали код, щоб перевірити нуль після проблеми минулого тижня, але користувач цього разу ввів порожню річ!"


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


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


2

Існує два види приватного коду: приватний код, який викликається за допомогою публічного коду (або приватний код, який викликається приватним кодом, який викликається за допомогою публічного коду (або ...)) і приватний код, який з часом не викликається публічним код.

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

Зауважте, що коли ви робите TDD, неможливо існувати неперевірений приватний код.


У нашій системі 99% коду третього роду : приватний, не називається публічним кодом, і важливий для системи (лише мінімальна частина нашої системи має зовнішній, публічний API).
Wizard79

1
"Зверніть увагу, що коли ви робите TDD, неможливо існувати неперевірений приватний код." <- видалити тестовий випадок, не знаючи, що тест є єдиним тестом, який охоплює певну гілку. Гаразд, це більш "неперевірений" код, але досить просто побачити пізніше тривіальне рефакторинг, що змінює цей код ... тільки ваш тестовий набір вже не охоплює його.
Френк Ширар

2

Тестування одиниць - це тестування одиниць вашого коду. Ви самі визначаєте, що таке одиниця. Ваші колеги визначають одиниці як елементи API.

У будь-якому разі тестування API має спричинити і використання приватного коду. Якщо ви визначите покриття коду як показник прогресу тестування одиниць, ви закінчите тестувати весь свій код. Якщо якась частина коду не була досягнута, дайте своїм колегам три варіанти:

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

У нашій системі API - це лише мінімальна частина, яка дозволяє автоматизувати / віддалене управління стороннім додатком. Тестуючи лише API, існує 1% покриття коду ...
Wizard79
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.