Як слід організувати код тестування модуля C ++ для досягнення максимальної ефективності тестування одиниць?


47
  • Це питання не стосується фреймворків тестування блоків.
  • Це питання не стосується написання модульних тестів.
  • Це питання стосується того, куди слід поставити написаний код UT та як / коли / де його скласти та запустити.

В роботі ефективно з успадкованим кодом , Майкл Пір'я стверджує , що

хороші одиничні тести ... бігайте швидко

і це

Тест на одиницю, який займає 1/10 секунди, - це повільний одиничний тест.

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

Очевидно , що проблема в C ++ є те , що для «запуску» ваш модульний тест ( и ), ви повинні:

  1. Відредагуйте свій код (виробничий або одиничний тест, залежно від того, в якому "циклі" ви перебуваєте)
  2. Складіть
  3. Посилання
  4. Виконання ( ів ) запуску блоку

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

Як можна ефективно організувати код тестування модулів C ++, щоб він був ефективним для редагування (тестового) коду та для запуску тестового коду?


Перша , то проблема, щоб вирішити , де поставити код тесту блоку таким чином , щоб:

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

Друга тоді, пов'язана, проблема в тому , що для компіляції , так що зворотний зв'язок миттєво.

Екстремальні варіанти:

  • Кожен Unit-Test-Test-Unit живе в окремому файлі cpp, і цей файл cpp компілюється + пов'язується окремо (разом із файлом блоку вихідного коду, який він тестує), до одного виконуваного файлу, який потім виконує цей єдиний Unit Test.
    • (+) Це мінімізує час запуску (компіляція + посилання!) Для одного тестового блоку.
    • (+) Тест працює дуже швидко, тому що він тестує лише одну одиницю.
    • (-) Виконання всього набору потрібно запустити мільярд процесів. Може бути проблемою управління.
    • (-) Частота запуску процесу стане видимою
  • Інша сторона повинна мати - все-таки - один cpp-файл на тест, але всі тестові файли cpp (разом з кодом, який вони перевіряють!) Пов'язані в один виконуваний файл (на модуль / на проект / вибір на ваш вибір).
    • (+) Час компіляції все-таки буде добре, оскільки буде зібраний лише змінений код.
    • (+) Виконати весь набір легко, оскільки для запуску достатньо лише однієї програми.
    • (-) Набір посилань потребуватиме віків, оскільки кожна перекомпіляція будь-якого об'єкта призведе до повторного посилання.
    • (-) (?) Пробіг триватиме більше часу, хоча якщо всі тести одиниці швидкі, час повинен бути нормальним.

Отже, як обробляються тести одиниць С ++ ? Якщо я виконую лише цей матеріал щовечора / щогодини, друга частина насправді не має значення, але перша частина, а саме як "з'єднати" код UT з виробничим кодом, щоб розробникам було "природно" тримати і те, і інше фокус завжди має значення, я думаю. (Якщо розробники мають фокус коду UT, вони захочуть запустити його, що поверне нас до другої частини.)

Історії та досвід реального світу високо оцінені!

Примітки:

  • Це питання навмисно залишає не визначену систему платформи та make / project.
  • Питання з тегами UT & C ++ - це чудове місце для початку, але, на жаль, занадто багато питань, а особливо відповідей, занадто сильно зосереджені на деталях або конкретних рамках.
  • Деякий час тому я відповів на подібне запитання щодо структури для підсилювальних одиниць тестів. Я вважаю, що цієї структури не вистачає для "справжніх", швидких тестових одиниць. І я вважаю інше питання занадто вузьким, звідси це нове запитання.

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

3
@Closers: Чи можете ви надати аргументи, які призвели до закриття цього питання?
Люк Турей

Я не зовсім розумію, чому це довелося закрити. :-(Де ви гадаєте шукати відповіді на такі запитання, якщо ні на цьому форумі?

2
@Joe: Навіщо мені потрібно тестування одиниць, щоб знайти, чи щось складеться, коли компілятор все одно скаже мені?
Девід Торнлі

2
@David: Тому що ви хочете переконатися, що він не компілюється . Швидким прикладом може бути об'єкт конвеєра, який приймає вхід і виробляє вихід, Pipeline<A,B>.connect(Pipeline<B,C>)а компілювати, тоді як Pipeline<A,B>.connect(Pipeline<C,D>)не повинен компілювати: Тип виходу першого етапу несумісний з типом вводу другого етапу.
sebastiangeiger

Відповіді:


6

У нас є всі тести на одиницю (для модуля) в одному виконаному файлі. Тести складаються в групи. Я можу виконати один тест (або деякі тести) або групу тестів, вказавши ім'я (тест / група) у командному рядку бігу тесту. Система збирання може запускати групу "Build", тестовий відділ може запускати "All". Розробник може поставити деякі тести в групу типу "BUG1234", при цьому 1234 є номером трекера випуску справи, над якою він працює.


6

По-перше, я не погоджуюся з "1) Редагуйте свій (виробничий) код та свій Тестовий блок". Ви повинні змінювати лише одну за одною, інакше, якщо результат зміниться, ви не знатимете, хто з них викликав.

Мені подобається ставити одиничні тести в дерево каталогів, яке затінює основне дерево. Якщо у мене є /sources/componentA/alpha/foo.ccі /objects/componentA/beta/foo.o, то я хочу що - щось на зразок /UTest_sources/componentA/alpha/test_foo.ccі /UTest_objects/componentA/beta/test_foo.o. Я використовую те саме тіньове дерево для заглушок / макетних об'єктів та будь-яких інших джерел, які потребують тести. Будуть деякі крайні випадки, але ця схема значно спрощує речі. Хороший макрос редактора може без зусиль підтягувати тестове джерело поряд із джерелом тематики. Хороша система побудови (наприклад, GNUMake) може компілювати обидва і запускати тест за допомогою однієї команди (наприклад make test_foo), а також може керувати базовим мільйоном таких процесів - лише тих, джерела яких змінилися з часу останнього тестування - досить легко (у мене є ніколи не виявив, що запуск цих процесів є проблемою, це O (N)).

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


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