Як ви використовуєте блок-тест коду за допомогою графічних структур?


18

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

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

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


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


PPS - Ця додаткова заява про проблему спочатку була розміщена автором питання в коментарі нижче:

For all vertices N in forest F, for all vertices M, in F, such that if there are any walks between N and M they all must either use only edges labelled 'conflict' or 'requires'.


13
Точно так само ви перевірите будь-який інший метод. Ви ідентифікуєте всі "цікаві" тестові випадки для кожного методу і пишете для них одиничні тести. У вашому випадку вам доведеться створити графіки залежностей для кожної із "цікавих" структур.
Данк

@Dunk Ми продовжуємо думати, що у нас є всі хитрі теми, і тоді ми розуміємо, що певна структура викликає проблеми, про які ми не розглядали раніше. Тестування кожного хитрого, про що ми можемо придумати, - це те, що ми робимо, те, що я сподіваюся знайти, - це деякі вказівки / процедури для створення клопітких прикладів, можливо, з використанням приводимості основних форм тощо.
Sled

6
У цьому полягає проблема з будь-якою формою тестування. Все, що ви знаєте, - це тести, які ви думали про роботу. Це не означає, що ваш sw - це помилка лише тому, що проходять ваші тести. Кожен проект має ту саму проблему. Я перебуваю на завершальній стадії виконання свого поточного проекту, щоб ми могли розпочати виробництво. Типи помилок, з якими ми стикаємось зараз, як правило, є досить неясними. Наприклад, коли апаратне забезпечення все ще працює на специфікацію, але ледь-ледь і, коли поєднується з іншим обладнанням одночасно з тією ж проблемою, виникають проблеми; але лише іноді :( SW добре перевірений, але ми не все думали
Dunk

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

Виявлення циклу - це добре висвітлена тема (див. Кнут, а також деякі відповіді нижче), і рішення не передбачають великої кількості спеціальних випадків, тому спочатку слід визначити, що робить вашу проблему такою. Це пов'язано з протиріччями, які ви згадуєте? Якщо так, нам потрібна додаткова інформація про них. Якщо це результат вибору впровадження, можливо, вам доведеться перефактурувати, можливо, великим способом. По суті, це проблема дизайну, через яку вам доведеться продумати свій шлях, TDD - це неправильний підхід, який може перенести вас у лабіринт до того, як станете тупиком.
sdenham

Відповіді:


5

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

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

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

Нарешті, навчіться приймати, що кожен тест може лише підтвердити наявність помилок, але не і відсутність помилок.


5

1. Випадкове генерація тесту

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

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

2. Важкі складні деталі

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

3. Створити вичерпний список

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


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

@sdenham Як ти збираєшся перерахувати щось, що літерально містить нескінченну кількість можливих дійсних комбінацій? Я сподівався знайти щось за принципом "це найскладніші структури графіків, які часто вловлюють помилки у вашій реалізації". Я досить добре розумію домен, як це просто: For all vertices N in forest F, for all vertices M, in F, such that if there are any walks between N and M they all must either use only edges labelled 'conflict' or 'requires'.домен - це не проблема.
Санки

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

@ArtB: Вам слід змінити питання, щоб включити оновлення до заявки про проблему, яку ви надали тут. Крім того, це може допомогти констатувати, що це спрямовані ребра (якщо це так), і чи буде цикл вважатися помилкою в графіку, а не просто ситуацією, з якою алгоритм повинен працювати.
sdenham

4

Жодне тестування в цьому випадку не зможе бути достатнім, навіть тонни реальних даних або невідомість. 100% покриття коду або навіть 100% покриття контуру недостатньо для перевірки рекурсивних функцій.

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

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

foreach nodes as node
    foreach nodes as tmp
        tmp.status = unmarked

    tovisit = []
    tovisit.push(node)
    node.status = required

    while |tovisit| > 0 do
        next = tovisit.pop()
        foreach next.requires as requirement
            if requirement.status = unmarked
                tovisit.push(requirement)
                requirement.status = required
            else if requirement.status = blacklisted
                return false
        foreach next.collides as collision
            if collision.status = unmarked
                requirement.status = blacklisted
            else if requirement.status = required
                return false
return true

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

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

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

EDIT: Що цей алгоритм не доводить, чи ваш графік не містить циклів. Напрямні ациклічні графіки є добре вивченою темою, тому пошук готового алгоритму для підтвердження цієї властивості також повинен бути простим.

Як бачите, зовсім не потрібно винаходити колесо.


3

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

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


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


2

Що стосується такого важкого для тестування алгоритму, я б пішов на TDD, де ви будуєте алгоритм на основі тестів,

TDD коротше,

  • написати тест
  • бачити, що це не вдається
  • змінити код
  • переконайтесь, що всі тести проходять
  • рефактор

і повторити цикл,

У цій конкретній ситуації

  1. Перший тест - це графік з одним вузлом, де алгоритм не повинен повертати жодних циклів
  2. Другим буде графік трьох вузлів без циклу, коли алгоритм не повинен повертати жодних циклів
  3. Наступним буде використання трьох вузлових графіків із циклом, коли алгоритм не повинен повертати жодних циклів
  4. Тепер ви можете перевірити його на трохи складніший цикл залежно від можливостей

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

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


2

Моє розуміння проблеми, як спочатку було заявлено і потім оновлено коментарями у відповіді Макке, включає таке: 1) спрямовані обидва типи краю (залежності та конфлікти); 2) якщо два вузли з'єднані одним краєм, вони не повинні бути з'єднані іншим, навіть якщо він іншого типу або зворотно; 3) якщо шлях між двома вузлами можна побудувати шляхом змішування ребер різних типів, то це помилка, а не обставина, яка ігнорується; 4) Якщо існує шлях між двома вузлами з використанням ребер одного типу, то між ними не може бути іншого шляху, використовуючи ребра іншого типу; 5) цикли або одного типу ребер, або змішаних типів ребер не дозволені (з здогадки в застосуванні, я не впевнений, що цикли, що стосуються лише конфлікту, є помилкою, але ця умова може бути усунена, якщо ні.)

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

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

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

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

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

Якщо змішані контури можна просто ігнорувати, а не помилятись (умова 3), достатньо розглянути графіки залежності та конфлікту незалежно та перевірити, якщо вузол досяжний в одному, він не в іншому.

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

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

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

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

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