Випадкові дані в одиничних тестах?


136

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

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

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

Ще один колега додав:

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

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

(Або по черзі, чи це прийнятний метод написання одиничних тестів, і я та інший колега помиляємось?)


32
"випадкові значення означають, що тест справді не повторюється" не відповідає дійсності, оскільки тети будуть використовувати псевдовипадкові числа. Забезпечте однакове початкове насіння, отримайте ту саму послідовність «випадкових» тестів.
Raedwald

11
Анекдот: Я одного разу написав експортний клас CSV, і випадкове тестування виявило помилку, коли контрольні символи були розміщені в кінці комірки. Без випадкового тестування я б ніколи не думав додати це як тестовий випадок. Це завжди провалювалося? Ні. Це ідеальний тест? Ні. Це допомогло мені зловити та виправити помилку? Так.
Тизоїд

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

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

Відповіді:


72

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

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

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

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

Java http://functionaljava.org/

Скала (або Java) http://github.com/rickynils/scalacheck

Haskell http://www.cs.chalmers.se/~rjmh/QuickCheck/

.NET: http://blogs.msdn.com/dsyme/archive/2008/08/09/fscheck-0-2.aspx

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

Щасливого тестування!


1
+1 до цього. ScalaCheck виконує феноменальну роботу з генерування мінімізованих випадкових даних тестів повторюваним способом.
Даніель Шпієк

17
Це не випадково. Це довільно. Велика різниця :)
Апокаліпс

На жаль, тепер, здається, існує версія reductiotest.org, і Google не вказував мені більше ніде. Будь-яка ідея, де зараз?
Raedwald

Зараз це частина функціональної бібліотеки Java. Посилання відредаговано. Але я б просто скористався Scalacheck для тестування Java-коду.
Апокаліпс

ScalaCheck переїхав до GitHub. Оновлене посилання у відповідь. Це також корисно для Java, а не лише для Scala. (Стара посилання була code.google.com/p/scalacheck )
RobertB

38

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

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


26

Тут знаходиться будинок на півдорозі, який має деяке використання, а саме - висівати PRNG постійним. Це дозволяє генерувати "випадкові" дані, які повторюються.

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


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

Що означає PRNG?
системович

Генератор псевдовипадкових чисел
Декан

16

У книзі Beautiful Code є розділ під назвою «Красиві тести», де він проходить стратегію тестування алгоритму бінарного пошуку . Один абзац називається "Випадкові акти тестування", в якому він створює випадкові масиви для ретельного тестування алгоритму. Дещо з цього ви можете прочитати в Інтернеті на Google Книгах, сторінка 95 , але це чудова книга, яку варто мати.

Тому в основному це просто показує, що генерування випадкових даних для тестування є можливим варіантом.


16

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

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

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

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

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

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

Для рандомізованих тестів можна сказати багато, особливо добре написаних, тому не варто занадто швидко їх звільняти!


14

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

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

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

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


10

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


9
  • якщо це випадкове значення, і тест не вдається, нам потрібно: а) виправити об'єкт і б) змусити кожного разу перевіряти це значення, тому ми знаємо, що воно працює, але оскільки це випадково, ми не знаємо, яке значення було

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


9

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


2
але хіба це принципово не відрізняється від одиничних тестів? і робиться в інший час?
ендоліт

1
@endolith не існує закону фізики, який змушує вас у певний час запускати певні тести
user253751

1
@immibis Але є вагомі причини робити конкретні тести в певний час. Ви не запускаєте батарею тестових блоків кожного разу, коли користувач натискає кнопку "Гаразд".
ендоліт

5

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

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


5

Ви повинні запитати себе, яка мета вашого тесту.
Одиничні тести стосуються перевірки логіки, потоку коду та взаємодії об'єктів. Використовуючи випадкові величини, намагається досягти іншої мети, тим самим знижуючи тестову увагу та простоту. Це прийнятно з міркувань читабельності (генерування UUID, ідентифікаторів, ключів тощо).
Зокрема для одиничних тестів, я не можу згадати навіть коли цей метод виявив проблеми. Але я бачив багато проблем детермінізму (у тестах), які намагаються бути розумними зі випадковими значеннями і головним чином із випадковими датами.
Тестування Fuzz - це вірний підхід для інтеграційних тестів та тестів, що передаються в кінці .


Я додам, що використання випадкових вхідних даних для плавлення є поганою заміною плавлення, керованого покриттям, коли це можливо.
gobenji

1

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

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


0

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


0

Ми просто сьогодні натрапили на це. Я хотів псевдовипадкових (щоб це виглядало як стислі аудіодані за розміром). Я TODO сказав, що я також хочу детермінованих . rand () відрізнявся на OSX, ніж на Linux. І якщо я не засіву повторно, це може змінитися в будь-який час. Тож ми змінили його на детерміновану, але все-таки пседедо-випадкову: тест повторюваний настільки ж, наскільки використовуються консервовані дані (але зручніше писати).

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

Це все-таки кваліфікується випадково? Давайте поговоримо над пивом. :-)


0

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

  • Тест із фіксованими даними
  • Тест з випадковими даними
  • Згенеруйте випадкові дані один раз , а потім використовуйте їх як фіксовані дані

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

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


-2

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

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

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


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