Чи веде одиничне тестування до передчасного узагальнення (конкретно в контексті C ++)?


20

Попередні примітки

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

Я візьму те, що там, і що говорить: тестування одиниці в сенсі "тестування найменшої відокремленої одиниці програми", з якої насправді випливає це запитання

Проблема ізоляції

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

Micheal Feathers розповідає про концепцію шва : [WEwLC, p31]

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

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

Приклади

Тестовий блок - особливо в C ++ - вимагає від коду, що перевіряється, додати більше швів, які були б суворо задіяні для даної проблеми.

Приклад:

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

Питання

Я спробую кілька версій, які, сподіваюсь, запитують приблизно те саме:

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

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


8
Загальна пам'ятка полягає в тому, що будь-який візерунок може бути використаний надмірним чином, щоб перетворитись на антидіаграму. Те саме стосується TDD. Можна додати тестові інтерфейси минуло до точки зменшення віддачі, де перевірений код менший, ніж доданий узагальнений тестовий інтерфейс, а також у зону занадто низької вартості та вигод. Повсякденна гра з додатковими інтерфейсами для тестування, як ОС для глибокої космічної місії, може повністю пропустити своє ринкове вікно. Переконайтесь, що додане тестування знаходиться перед цими перегинами.
hotpaw2

@ hotpaw2 Блюзнірство! :)
maple_shaft

Відповіді:


23

Тестовий блок - особливо в C ++ - вимагає від коду, що перевіряється, додати більше швів, які були б суворо задіяні для даної проблеми.

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

У апаратному світі це дізналися давно - важким шляхом. Виробники різного обладнання протягом століть дізналися від незліченних падаючих мостів, вибуху автомобілів, куріння процесорів тощо., Що ми вивчаємо зараз у світі програмного забезпечення. Усі вони створюють «зайві шви» у своїх продуктах, щоб зробити їх відчутними. Більшість нових автомобілів сьогодні мають діагностичні порти для ремонтників, щоб отримати дані про те, що відбувається всередині двигуна. Значна частина транзисторів на кожному процесорі служить діагностичним цілям. У апаратному світі кожен шматочок «зайвих» речей коштує, а коли продукт виготовляється мільйонами, ці витрати, безумовно, становлять великі суми грошей. Все-таки виробники готові витратити всі ці гроші на доказовість.

Повернувшись до світу програмного забезпечення, C ++ справді складніше перевірити, ніж пізніші мови, що демонструють динамічне завантаження класів, рефлексію тощо. Тим не менш, більшість питань можна принаймні пом'якшити. У одному проекті C ++, де я використовував тестові одиниці, ми не виконували тести так часто, як ми, наприклад, в проекті Java, але все-таки вони були частиною нашої побудови CI, і ми вважали їх корисними.

Чи спосіб, який вимагає тестування одиничних тестів, щоб структурувати код програми "тільки", вигідний для одиничних тестів, чи це фактично вигідно структурі додатків?

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

  • Здійснення тестування вашого дизайну змушує згуртовувати вашу програму на невеликі, більш-менш незалежні частини, які можуть впливати один на одного обмеженими та чітко визначеними способами - це дуже важливо для довгострокової стабільності та ремонтопридатності вашої програми. Без цього код має тенденцію до погіршення коду спагетті, коли будь-яка зміна, внесена в будь-яку частину кодової бази, може спричинити несподівані наслідки у, здавалося б, не пов'язаних між собою окремих частинах програми. Що, безсумнівно, - це кошмар кожного програміста.
  • Написання самих тестів в режимі TDD насправді виконує ваші API, класи та методи, і служить дуже ефективним тестом для визначення того, чи має ваш дизайн сенс - якщо писати тести проти та інтерфейс відчуває себе незручно чи важко, ви отримуєте цінний ранній відгук, коли він все ще легко формувати API. Іншими словами, це захищає вас перед публікацією своїх API раніше.
  • Шаблон розвитку, що застосовується TDD, допомагає вам зосередитись на конкретній задачі, яку потрібно виконати, і тримає вас на цілі, мінімізуючи шанси на те, що ви не будете вирішувати інші проблеми, ніж ті, що вам належить, додаючи зайві додаткові функції та складність тощо.
  • Швидкий зворотний зв'язок одиничних тестів дозволяє сміливо рефакторинг коду, що дозволяє вам постійно адаптувати та розвивати дизайн протягом усього життя коду, тим самим ефективно запобігаючи ентропії коду.

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

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

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


Сер, вітаю вас.
GordonM

Коротка, але педантична примітка: "діагностичний порт" здебільшого є тому, що уряди зобов'язали їх як частину схеми контролю викидів. Отже, вона має суворі обмеження; Є багато речей, які потенційно могли б діагностувати цей порт, якого немає (тобто нічого, що не має відношення до контролю викидів).
Роберт Харві

4

Я збираюся кинути на тебе Тестівський шлях , але підсумую:

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

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

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

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

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


Я збирався додати те саме: зробити апі, випробувати api, ззовні.
Крістофер Махан

2

TDD і Unit Testing - добре для програми в цілому, а не тільки для одиничних тестів. Причина цього в тому, що це добре для мозку.

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

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


1

тестування найменшого відокремленого блоку програми

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

Наприклад, я одного разу налагодив мережевий клас, який мав (серед інших бітів) 2 способи: 1 встановити IP-адресу, інший - встановити номер порту. Звичайно, це були дуже прості методи і легко проходили б найтривіальніший тест, але якщо встановити номер порту, а потім встановити ip адресу, він не спрацює - ip setter замінив номер порту за замовчуванням. Тож вам довелося перевірити клас у цілому, щоб переконатися у правильній поведінці. Щось, на мою думку, концепція TDD відсутня, але BDD дає вам. Вам не потрібно перевіряти кожен крихітний метод, коли ви можете протестувати найрозумнішу та найменшу область загальної програми - у цьому випадку клас мереж.

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

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

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


0

Ви добре зрозуміли, що стосується мовного дизайну. Деякі основні дизайнерські рішення C ++ (механізм віртуальної функції, змішаний із механізмом статичного виклику функції) роблять TDD жорстким. Мова насправді не підтримує те, що потрібно, щоб полегшити її. Скласти С ++ легко, що майже неможливо одиничним тестом.

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

Ключовим є квазіфункціональний підхід. Подумайте про це, якщо весь ваш C ++ код був безкоштовними функціями, які не мали доступу до глобальних точок, це було б одразу для одиничного тесту :)

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