Чи важливіші твердження чи одиничні тести?


51

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


4
Мені дуже подобається ця ідея ... настільки, що я роблю тему завданням для мого тестування програмного забезпечення та курсу забезпечення якості. :-)
Macneil

Інше питання: чи варто ви перевірити свої твердження? ;)
моджуба

Відповіді:


43

Твердження корисні для того, щоб розповісти про внутрішній стан програми . Наприклад, що ваші структури даних мають дійсний стан, наприклад, що Timeструктура даних не буде містити значення 25:61:61. Умови, що перевіряються твердженнями:

  • Передумови, які запевняють, що абонент дотримується свого контракту,

  • Пост-умови, які запевняють, що замовник зберігає свій контракт, і

  • Інваріанти, які запевняють, що структура даних завжди містить деяку властивість після повернення функції. Інваріант - це умова, яка є передумовою і постумовою.

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

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

У тестуванні може спостерігатися певне перекриття. Наприклад, Stackпост-умова 's може фактично стверджувати, що розмір стека збільшується на одиницю. Але є обмеження в тому, що можна виконати в цьому твердженні: чи слід також перевірити, що верхній елемент - це те, що тільки що було додано?

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

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

Обидва важливі і мають свої цілі.

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


7

Говорячи про твердження, майте на увазі, що їх можна вимкнути при натисканні перемикача.

Приклад дуже поганого твердження:

char *c = malloc(1024);
assert(c != NULL);

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

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

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

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

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

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


3
Ось чому твердження завжди повинні бути правдивими твердженнями, а не тестуванням помилок.
Домінік МакДоннелл

@DominicMcDonnell Ну, "повинні бути правдивими" твердження. Я іноді запевняю, щоб обійти компілятори, такі як певні версії gcc, які мають вбудований глюкозний abs (). Важливо пам’ятати, що виробничі збірки повинні їх вимкнути в будь-якому випадку .
Tim Post

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

@Frank Shearar, дуже правда. Ви все одно повинні бути невдалі при найперших виявлених помилках виробництва. Впевнені, що користувачі збираються скаржитися, але це єдиний спосіб переконатися, що помилки збираються виправити. І це набагато краще, щоб отримати blah було 0, ніж виняток з пам'яті для виведення нуля на кілька функцій дзвінків пізніше.
Домінік МакДоннелл

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

5

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

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

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

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

Особисто я вважаю, що отримую більше пробігу за допомогою тестування одиниць, ніж посипання тверджень, але це через платформи, які я розробляю більшу частину часу (Java / C #). Інші мови мають більш надійну підтримку тверджень і навіть "Дизайн за контрактом" (див. Нижче), щоб забезпечити ще більше гарантій. Якщо я працював з однією з цих мов, я міг би використовувати DBC більше, ніж тестування одиниць.

http://en.wikipedia.org/wiki/Design_by_contract#Languages_with_native_support

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