Чи достатньо використовувати тести прийняття та інтеграції замість одиничного тесту?


62

Короткий вступ до цього питання. Я використовую TDD і останнім часом BDD вже більше року. Я використовую такі методи, як глузування, щоб зробити письмові тести ефективніше. Останнім часом я розпочав особистий проект, щоб написати для себе невелику програму управління грошима. Оскільки у мене не було застарілого коду, це був ідеальний проект, який можна було розпочати з TDD. На жаль, я не пережив такої радості від TDD. Це навіть настільки зіпсувало мою забаву, що я відмовився від проекту.

У чому була проблема? Ну, я використовував подібний підхід TDD, щоб тести / вимоги розвивали дизайн програми. Проблема полягала в тому, що більше половини часу на розробку, як для написання / тестування рефактора. Отже, врешті-решт, я не хотів реалізовувати більше функцій, тому що мені потрібно було б переробляти та писати на багато тестів.

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

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

Після того, як я шукав через Інтернет , я виявив, що є дуже схожі ідеї , по-моєму , згадані тут , і там . Що ви думаєте про цю ідею?

Редагувати

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

Спочатку були певні вимоги до виконання певних команд. Я написав розширюваний командний аналізатор - який розбирав команди з якогось командного рядка і називав правильний на моделі. Результат був представлений у модельному класі перегляду: Перший дизайн

Тут нічого поганого не було. Усі класи були незалежними один від одного, і я міг легко додавати нові команди, показувати нові дані.

Наступна вимога полягала в тому, щоб кожна команда мала своє представлення подання - якесь попереднє перегляд результату команди. Я переробив програму, щоб досягти кращого дизайну для нової вимоги: Другий дизайн

Це було також добре, оскільки тепер у кожної команди є своя модель перегляду, а отже, і власний попередній перегляд.

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

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

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

Висновок

Дякую за всі відповіді та обговорення. Підсумовуючи цю дискусію, я продумав підхід, який я випробую під час свого наступного проекту.

  • Перш за все, я пишу всі тести, перш ніж реалізувати щось подібне, як завжди.
  • Для вимог я спочатку пишу кілька тестів на прийняття, які перевіряють всю програму. Потім я пишу кілька тестів на інтеграцію для компонентів, де мені потрібно реалізувати вимогу. Якщо є компонент, який тісно співпрацює з іншим компонентом, щоб реалізувати цю вимогу, я також написав би тести інтеграції, де обидва компоненти тестуються разом. І останнє, але не в останню чергу, якщо мені доведеться написати алгоритм або будь-який інший клас з високою перестановкою - наприклад, серіалізатором - я б писав одиничні тести для цього конкретного класу. Усі інші класи не перевіряються, але будь-які одиничні тести.
  • Для помилок процес можна спростити. Зазвичай помилка викликається одним або двома компонентами. У цьому випадку я б написав один інтеграційний тест для компонентів, які тестують помилку. Якби це стосувалося алгоритму, я б написав лише одиничний тест. Якщо виявити компонент, де виникає помилка, непросто, я написав би тест на прийняття, щоб знайти помилку - це має бути винятком.


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

на моє читання, відповіді у двох примірниках питання стосуються передусім того, коли тести, що не є єдиними, мають більше сенсу
gnat

Ця перша посилання сама по собі є дублікатом. Подумайте, ви маєте на увазі: programmers.stackexchange.com/questions/66480/…
Роббі Ді

Відповіді за посиланням від Роббі Ді - ще більше про те, навіщо взагалі тестуватись.
Yggdrasil

Відповіді:


37

Це порівняння апельсинів і яблук.

Інтеграційні тести, приймальні тести, одиничні тести, тести поведінки - це все тести, і всі вони допоможуть вам покращити свій код, але вони також зовсім інші.

Я збираюся переглянути кожен з різних тестів на мою думку і, сподіваюся, поясню, для чого вам потрібна суміш усіх:

Інтеграційні тести:

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

Тести приймання:

Тест на прийняття повинен безпосередньо співвідноситись із випадком використання бізнесу. Він може бути величезним ("торги подані правильно") або крихітним ("фільтр успішно фільтрує список") - це не має значення; Що важливо, це те, що він повинен бути явно прив'язаний до конкретної потреби користувача. Мені подобається зосередитись на них на тестових розробках, оскільки це означає, що ми маємо хороший посібник з тестів на історії користувачів для dev і qa для перевірки.

Тестові одиниці:

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

Тести поведінки:

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

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


У вашому визначенні я припускаю, що я маю на увазі написати більше тестів на поведінку (?), Ніж тест на одиницю. Тест одиниці для мене - це тест, який тестує один клас із знущаннями з усіх залежностей. Бувають випадки, коли одиничні тести є найбільш корисними. Це, наприклад, коли я пишу складний алгоритм. Тоді у мене є багато прикладів із очікуваним виходом алгоритму. Я хочу перевірити це на рівні одиниць, оскільки це насправді швидше, ніж тест на поведінку. Я не бачу значення тестування класу на рівні одиниць, у якого лише рука повна стежками через клас, які легко перевірити тестом поведінки.
Іггдрасіль

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

Я не настільки твердий з вашою термінологією. Ми програмуємо набір програм. Там я відповідаю за графічного редактора. Мої тести тестували редактор з глузуючими службами з решти набору та з глузливим інтерфейсом користувача. Що це за тест?
Іггдрасіль

1
Залежить від того, що ви тестуєте - ви тестуєте особливості бізнесу (тести прийняття)? Ви протестуєте інтеграцію (інтеграційні тести)? Ви тестуєте те, що відбувається при натисканні на кнопку (тести поведінки)? Ви перевіряєте алгоритми (одиничні тести)?
Михайло

4
"Мені не подобається орієнтуватися на підходи до підручників - скоріше, зосереджуйтеся на тому, що дає вашим тестам значення" О, так правда! Перше питання, яке завжди потрібно задати - "яку проблему я вирішую, роблячи це?". І різні проекти можуть мати різні проблеми для вирішення!
Лоран Бургу-Рой

40

TL; DR: Поки це відповідає вашим потребам, так.

Я вже багато років займаюся розробкою тесту на прийняття тесту на прийняття (ATDD). Це може бути дуже успішним. Слід пам’ятати кілька речей.

  • Експертні тести дійсно допомагають виконувати МОК. Без тестових одиниць, onus на розробників, щоб переконатися, що вони відповідають вимогам добре написаного коду (якщо одиничні тести керують добре написаним кодом)
  • Вони можуть бути повільнішими та мати помилкові збої, якщо ви фактично використовуєте ресурси, які зазвичай знущаються.
  • Тест не точно визначив конкретну проблему, як це могли б одиничні тести. Вам потрібно зробити більше розслідування, щоб виправити помилки тесту.

Тепер переваги

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

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


8
Відмінний момент. Це занадто просто, щоб стати трохи космічним кадетистом щодо тестування і написати сотні справ, щоб отримати тепле сяйво задоволення, коли воно "проходить" літаючими фарбами. Простіше кажучи: якщо ваше програмне забезпечення не робить те, що потрібно робити з точки зору КОРИСТУВАЧА, ви провалили перший і найважливіший тест.
Роббі Ді

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

2
"Одиничні тести допомагають застосувати МОК"?!? Я впевнений, що ви мали на увазі DI там замість IoC, але все одно, чому б хтось хотів застосувати використання DI? Особисто я вважаю, що на практиці DI призводить до не-об'єктів (програмування в стилі процедур).
Rogério

Чи можете ви дати свій внесок щодо (найкращого IMO) варіанту проведення BOTH інтеграції та одиничного тестування проти аргументу лише тестування інтеграції? Ваша відповідь тут хороша, але, здається, обрамляє ці речі як взаємовиключні, що я не вважаю, що вони є.
starmandeluxe

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

18

Ну, я використовував подібний підхід TDD, щоб тести / вимоги розвивали дизайн програми. Проблема полягала в тому, що більше половини часу на розробку, як для написання тестів / рефактор

Блокові тести працюють найкраще, коли публічний інтерфейс компонентів, для яких вони використовуються, не змінюється занадто часто. Це означає, що компоненти вже сконструйовані добре (наприклад, дотримуючись принципів SOLID).

Тож вважати, що хороший дизайн просто "розвивається" від "кидання" багатьох одиниць тестів на компонент - це помилка. TDD не є «викладачем» для гарного дизайну, він може лише трохи допомогти переконатися, що певні аспекти дизайну є хорошими (особливо доказовими).

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

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

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

РЕДАКТУВАННЯ: Іноді дизайн ваших компонентів може бути чудовим, але дизайн ваших тестових блоків може спричинити проблеми . Простий приклад: Ви хочете перевірити метод "MyMethod" класу X і записати

    var x= new X();
    Assert.AreEqual("expected value 1" x.MyMethod("value 1"));
    Assert.AreEqual("expected value 2" x.MyMethod("value 2"));
    // ...
    Assert.AreEqual("expected value 500" x.MyMethod("value 500"));

(припустимо, що значення мають якесь значення).

Припустимо, що у виробничому коді є лише один дзвінок X.MyMethod. Тепер для нової вимоги методу "MyMethod" потрібен додатковий параметр (наприклад, щось на зразок context), який неможливо опустити. Без одиничних тестів доведеться перефактурувати код виклику лише в одному місці. За допомогою одиничних тестів потрібно переробляти 500 місць.

Але причина тут не в одиничних тестах, а лише в тому, що той самий виклик до "X.MyMethod" повторюється знову і знову, не строго слідуючи принципу "Не повторюй себе (DRY)". Тому рішення тут потрібно помістити дані тесту та пов'язані з ним очікувані значення у список та запустити виклики до "MyMethod" у циклі (або, якщо інструмент тестування підтримує так звані "тести накопичувача даних", використовувати цю функцію). Це зменшує кількість місць для зміни одиничних тестів, коли підпис методу змінюється на 1 (на відміну від 500).

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


"Це означає, коли компоненти вже спроектовані добре.": Я згоден з вами, але як можна складати компоненти, якщо ви пишете тести перед написанням коду, а код - це дизайн? Принаймні, так я зрозумів TDD.
Джорджіо

2
@Giorgio: насправді це не має особливого значення, чи ви пишете тести спочатку чи пізніше. Проектування означає приймати рішення про відповідальність компонента, про загальнодоступний інтерфейс, про залежності (прямі або вводиться), про час виконання або поведінку часу компіляції, про мутабельність, про імена, про потік даних, про потік управління, шари тощо. Добре дизайн також означає відкласти деякі рішення до останнього можливого моменту часу. Тестовий пристрій може показати вам опосередковано, якщо ваша конструкція була в порядку: якщо ви згодом переробляєте багато з них після зміни вимог, ймовірно, це не було.
Док Браун

@Giorgio: приклад може уточнити: скажімо, у вас є компонент X із методом "MyMethod" та 2 параметрами. Використовуючи TDD, ви пишете, X x= new X(); AssertTrue(x.MyMethod(12,"abc"))перш ніж реально реалізувати метод. Використовуючи попередній дизайн, ви можете написати class X{ public bool MyMethod(int p, string q){/*...*/}}спочатку, а тести - пізніше. В обох випадках ви прийняли однакове дизайнерське рішення. Якщо рішення було хорошим чи поганим, TDD вам не скаже.
Док Браун

1
Я згоден з вами: я трохи скептичний, коли бачу, як TDD застосовується наосліп з припущенням, що це автоматично створить хороший дизайн. Крім того, іноді TDD заважає, якщо дизайн ще не зрозумілий: я змушений перевірити деталі, перш ніж мати огляд того, що я роблю. Отже, якщо я правильно розумію, ми погоджуємось. Я думаю, що (1) тестування блоків допомагає перевірити дизайн, але дизайн - це окрема діяльність, і (2) TDD - це не завжди найкраще рішення, тому що вам потрібно організувати свої ідеї, перш ніж починати писати тести, і TDD може сповільнити вас це.
Джорджіо

1
Незабаром, одиничні тести можуть показати недоліки у внутрішній конструкції компонента. Інтерфейс, умови до та після нього необхідно знати заздалегідь, інакше не вдасться створити блок-тест. Таким чином, дизайн компонента, який він виконує, потрібно виконати до того, як можна буде записати тест одиниці. Як це робиться - нижчий рівень проектування, детальний дизайн або внутрішній дизайн або все, що ви хочете назвати - може відбутися після того, як було написано одиничне тестування.
Maarten Bodewes

9

Так, звичайно.

Врахуйте це:

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

Дивіться загальну різницю ....

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

Я думаю, що вам може знадобитися їх змішати, оскільки кожен проект, що базується на TDD, потребує певного інтеграційного тестування, щоб переконатися, що всі підрозділи дійсно добре працюють разом (я з досвіду знаю, що кодова база даних, перевірена на 100%, не обов'язково працює коли ви їх зведете разом!)

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

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


7
Не зовсім правда ... За допомогою тестів на інтеграцію можливо мати два компоненти, які обидва баггі, але їх помилки скасовуються в тесті інтеграції. Це не має великого значення, поки кінцевий користувач не використає його таким чином, як використовується лише один із цих компонентів ...
Michael Shaw

1
Покриття коду! = Тестовано - окрім помилок, які скасовують один одного, а як щодо сценаріїв, про який ви ніколи не думали? Тестування на інтеграцію чудово підходить для тестування на щасливому шляху, але я рідко бачу адекватне тестування на інтеграцію, коли справи йдуть не так.
Майкл

4
@Ptolemy Я думаю, що рідкість двох помилкових компонентів, що відміняють один одного, є набагато нижчим, ніж 2 робочих компоненти, які заважають один одному.
gbjbaanb

2
@Michael, то ви просто не доклали достатньо зусиль для тестування, я сказав, що важче зробити хороше інтеграційне тестування, оскільки тести повинні бути набагато детальнішими. Ви можете надати погані дані в тесті інтеграції так само легко, як і в одиничному тесті. Інтеграційне тестування! = Щасливий шлях. Йдеться про використання максимально можливого коду, тому існують інструменти для покриття коду, які показують, яку частину вашого коду було використано.
gbjbaanb

1
@Michael Коли я правильно використовую такі інструменти, як Cucumber або SpecFlow, я можу створити інтеграційні тести, які також тестують винятки та екстремальні ситуації так само швидко, як одиничні тести. Але я згоден, якщо в одному класі є багато перестановок, я вважаю за краще писати одиничний тест для цього класу. Але це буде рідше, ніж заняття з лише кількома стежками.
Іггдрасіль

2

Я думаю, що це жахлива ідея.

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

Ні, зазвичай слід писати більше одиничних тестів, якщо у вас немає додаткового додатка, який становить 90% інтерфейс користувача, або чогось іншого, що незрозуміло для одиничного тесту. Біль, з якою ви стикаєтеся, не полягає в одиничних тестах, але робиться для початку тестування. Як правило, вам слід витратити лише 1/3 свого часу на більшість письмових тестів. Зрештою, вони там, щоб служити вам, а не навпаки.


2
Основна яловичина, яку я чую від TDD, це те, що вона порушує природний потік розвитку та застосовує оборонне програмування з самого початку. Якщо програміст вже знаходиться під тиском часу, вони, ймовірно, захочуть просто скоротити код і відшліфувати його пізніше. Звичайно, дотримання довільного терміну з баггі-кодом - помилкова економія.
Роббі Ді

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

3
Відповідь має сенс для мене, не знаю, чому у неї стільки мінусів. Цитуючи Майка Конна: "Тестування одиниць повинно бути основою міцної стратегії автоматизації тестування і як такої являє собою найбільшу частину піраміди. Автоматизовані тестові одиниці чудові тим, що вони дають конкретні дані програмісту - є помилка і вона працює на рядок 47 " Mountaingoatsoftware.com/blog/…
guillaume31

4
@ guillaume31 Тільки тому, що хтось один раз сказав, що вони хороші, це не означає, що вони хороші. На мій досвід, помилки НЕ виявляються одиничними тестами, оскільки вони були змінені на нову вимогу наперед. Також більшість помилок є помилками інтеграції. Для цього я виявляю більшість із них за допомогою моїх інтеграційних тестів.
Іггдрасіль

2
@Yggdrasil Я можу також процитувати Мартіна Фаулера: "Якщо ви отримаєте невдачу в тесті високого рівня, ви маєте не просто помилку у своєму функціональному коді, у вас також є відсутність тестового блоку". martinfowler.com/bliki/TestPyramid.html У будь-якому разі, якщо інтеграційні тести працюють лише для вас, то добре. Мій досвід полягає в тому, що, хоча це потрібно, вони повільніше, доставляють менш точні повідомлення про відмову і менш маневрені (більш комбінаторні), ніж одиничні тести. Крім того, я, як правило, менш захищений від майбутнього, коли пишу інтеграційні тести - міркування про заздалегідь задумані сценарії, а не правильність об'єкта як такої.
guillaume31

2

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

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

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

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

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

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


1
Я пишу свої тести заздалегідь і пишу достатньо тестів, щоб покрити більшість помилок. Що стосується помилок, поверхня яких пізніше. Мені це звучить, що вимога змінилася або вступила нова вимога. Чим тести на інтеграцію / поведінку потрібно змінювати, додавати. Якщо тоді помилка відображається в старій вимозі, мій тест на це покаже її. Щодо автоматизації. всі мої тести працюють весь час.
Yggdrasil

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

2
Автоматизація тестування та тестування блоків є повністю ортогональною справою. Будь-який поважаючий себе проект матиме автоматизовану інтеграцію та функціональне тестування. Зрозуміло, ви не часто бачите тести ручних одиниць, але вони можуть існувати (в основному ручний тест одиниці є корисною програмою для тестування певної функції).
Ян Худек

Ну справді. Існує процвітаючий ринок автоматизованих сторонніх інструментів, які існують поза сферою розвитку протягом значного часу.
Роббі Ді

1

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

Це ключовий момент, а не лише «остання перевага». Коли проект стає все більшим і більшим, ваші тести на прийняття інтеграції стають все повільнішими та повільнішими. І тут я маю на увазі так повільно, що ви перестанете їх виконувати.

Звичайно, одиничні випробування також стають повільнішими, але вони все ж більш, ніж на порядок швидші. Наприклад, у моєму попередньому проекті (c ++, приблизно 600 кЛОК, 4000 одиничних тестів та 200 інтеграційних тестів) на виконання інтеграційних тестів знадобилося близько однієї хвилини, а понад 15 - на. Для складання та виконання одиничних тестів для частини, що змінюється, в середньому знадобиться менше 30 секунд. Коли ви зможете зробити це так швидко, вам захочеться робити це постійно.

Просто для того, щоб було зрозуміло: я не кажу не додавати тести на інтеграцію та прийняття, але схоже, що ви зробили TDD / BDD неправильно.

Тест блоку також хороший для дизайну.

Так, проектування з урахуванням достовірності зробить дизайн кращим.

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

Що ж, коли вимоги змінюються, вам доведеться змінити код. Я б сказав, що ви не закінчили свою роботу, якби не написали одиничні тести. Але це не означає, що ви повинні мати 100% покриття одиничними тестами - це не мета. Деякі речі (наприклад, GUI або доступ до файлу, ...) навіть не призначені для перевірки на одиницю.

Результатом цього є краща якість коду та ще один рівень тестування. Я б сказав, що воно того варте.


У нас також було кілька тестів прийняття 1000-х, і на це пішло б цілий тиждень.


1
Ви подивилися на мій приклад? Це відбувалося весь час. Справа в тому, що коли я реалізую нову функцію, я змінюю / додаю тест одиниці так, щоб вони перевіряли нову функцію - тому жоден одиничний тест не буде порушений. У більшості випадків у мене виникають побічні ефекти змін, які не виявляються одиничними тестами - тому що над середовищем глузують. З мого досвіду через це жоден одиничний тест ніколи не сказав мені, що я порушив існуючу функцію. Завжди тести на інтеграцію та прийняття показали мені мої помилки.
Іггдрасіль

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

@Yggdrasil Як я вже сказав, одиничні тести не всі могутні, але вони, як правило, є першим шаром тестування, оскільки вони найшвидші. Інші тести також корисні, і їх слід комбінувати.
BЈоviћ

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

1
Питання полягало в тому, яке значення я маю від одиничного тесту, коли вони не ламаються? Навіщо морочитися писати їх, коли мені завжди потрібно підлаштовувати їх під нові вимоги? Єдине значення, яке я бачу, - це алгоритми та інші класи з високою перестановкою. Але це менше, ніж складові та приймальні тести.
Yggdrasil
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.