Тестування одиниці властивих випадкових / недетермінованих алгоритмів


41

Мій поточний проект, лаконічно, передбачає створення "обмежено випадкових подій". Я в основному генерую графік перевірок. Деякі з них базуються на суворих обмеженнях графіку; Ви проводите огляд раз на тиждень у п’ятницю о 10:00 ранку. Інші перевірки є "випадковими"; є основні налаштовані вимоги, такі як "інспекція повинна відбуватися 3 рази на тиждень", "інспекція повинна відбуватися між годинами 9 AM-9PM", і "не повинно бути двох перевірок протягом одного 8-годинного періоду", але в межах будь-яких обмежень, налаштованих для певного набору перевірок, отримані дати та час не повинні бути передбачуваними.

Тестові одиниці та TDD, IMO мають велику цінність у цій системі, оскільки їх можна використовувати для поступового її побудови, поки її повний набір вимог ще неповний, і переконайтесь, що я не "переробляю" це для того, щоб робити те, що мені не подобається На даний момент я не знаю, що мені потрібно. Суворі графіки були торт-торт до TDD. Однак мені важко реально визначити те, що я тестую, коли пишу тести для випадкової частини системи. Я можу стверджувати, що всі часи, що виробляються планувальником, повинні підпадати під обмеження, але я міг би реалізувати алгоритм, який проходить усі такі тести, не будучи фактичними "випадковими" часом. Насправді саме так і сталося; Я знайшов проблему, коли час, хоча і не передбачуваний точно, потрапляв у невелику підмножину допустимих діапазонів дати / часу. Алгоритм все-таки передав усі твердження, які я вважав, що можу розумно висловитись, і не міг розробити автоматизований тест, який би вийшов з ладу в цій ситуації, але здав, коли давали "більш випадкові" результати. Мені довелося продемонструвати, що проблема була вирішена шляхом реструктуризації деяких існуючих тестів, щоб повторити їх кілька разів, і візуально перевірити, чи згенерований час потрапив у повний допустимий діапазон.

Хтось має поради щодо розробки тестів, які повинні очікувати недетермінованої поведінки?


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

Я створив набір тестів «пісочниці», що містять алгоритми-кандидати для обмежувального процесу (процес, за допомогою якого байтовий масив, який міг би бути будь-яким довгим, стає довгим між хв і макс. Потім я запускаю цей код через цикл FOR, який дає алгоритму кілька відомих байтових масивів (значення від 1 до 10 000 000 лише для початку), і алгоритм обмежує кожен до значення між 1009 і 7919 (я використовую прості числа для забезпечення алгоритм не проходив би через деякий безпрограшний GCF між вхідним та вихідним діапазонами). Отримані обмежені значення підраховуються та виробляється гістограма. Щоб "пройти", всі вхідні дані повинні бути відображені у гістограмі (обґрунтовано, щоб ми не "втратили" жодне), і різниця між будь-якими двома відрами в гістограмі не може бути більшою за 2 (вона дійсно повинна бути <= 1 , але будьте в курсі). Алгоритм виграшу, якщо такий є, можна вирізати та вставити безпосередньо у виробничий код та встановити постійний тест для регресії.

Ось код:

    private void TestConstraintAlgorithm(int min, int max, Func<byte[], long, long, long> constraintAlgorithm)
    {
        var histogram = new int[max-min+1];
        for (int i = 1; i <= 10000000; i++)
        {
            //This is the stand-in for the PRNG; produces a known byte array
            var buffer = BitConverter.GetBytes((long)i);

            long result = constraintAlgorithm(buffer, min, max);

            histogram[result - min]++;
        }

        var minCount = -1;
        var maxCount = -1;
        var total = 0;
        for (int i = 0; i < histogram.Length; i++)
        {
            Console.WriteLine("{0}: {1}".FormatWith(i + min, histogram[i]));
            if (minCount == -1 || minCount > histogram[i])
                minCount = histogram[i];
            if (maxCount == -1 || maxCount < histogram[i])
                maxCount = histogram[i];
            total += histogram[i];
        }

        Assert.AreEqual(10000000, total);
        Assert.LessOrEqual(maxCount - minCount, 2);
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionMSBRejection()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByMSBRejection);
    }

    private long ConstrainByMSBRejection(byte[] buffer, long min, long max)
    {
        //Strip the sign bit (if any) off the most significant byte, before converting to long
        buffer[buffer.Length-1] &= 0x7f;
        var orig = BitConverter.ToInt64(buffer, 0);
        var result = orig;
        //Apply a bitmask to the value, removing the MSB on each loop until it falls in the range.
        var mask = long.MaxValue;
        while (result > max - min)
        {
            mask >>= 1;
            result &= mask;
        }
        result += min;

        return result;
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionLSBRejection()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByLSBRejection);
    }

    private long ConstrainByLSBRejection(byte[] buffer, long min, long max)
    {
        //Strip the sign bit (if any) off the most significant byte, before converting to long
        buffer[buffer.Length - 1] &= 0x7f;
        var orig = BitConverter.ToInt64(buffer, 0);
        var result = orig;

        //Bit-shift the number 1 place to the right until it falls within the range
        while (result > max - min)
            result >>= 1;

        result += min;
        return result;
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionModulus()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByModulo);
    }

    private long ConstrainByModulo(byte[] buffer, long min, long max)
    {
        buffer[buffer.Length - 1] &= 0x7f;
        var result = BitConverter.ToInt64(buffer, 0);

        //Modulo divide the value by the range to produce a value that falls within it.
        result %= max - min + 1;

        result += min;
        return result;
    }

... і ось результати:

введіть тут опис зображення

Відхилення LSB (зміщення біта числом, поки воно не потрапить у діапазон) було ТЕРМІННО, з дуже простої для пояснення причини; коли ви розділите будь-яке число на 2, поки воно не буде меншим за максимум, ви вийдете, як тільки воно є, і для будь-якого нетривіального діапазону це змістить результати до верхньої третини (як це було видно в детальних результатах гістограми ). Саме таку поведінку я бачив із закінчених дат; всі часи були вдень, у дуже конкретні дні.

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

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


2
Отже, врешті-решт, ви хочете, щоб програма, яка шукає вихід генератора випадкових чисел і вирішила, чи є вона випадковою? Як у "5,4,10,31,120,390,2,3,4" було випадковим, але "49,39,1,10,103,12,4,189" не було?
пн

Ні, але уникнути введення зміщення між фактичним PRNG та кінцевим результатом було б добре.
KeithS

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

Також слід перевірити на комбінації. Приблизно однакові очікувані перевірки за годину не захищатимуть справи, коли, скажімо, за обстеженням на 11:00 у вівторок завжди слідують 2 вечора в четвер і 10 ранку в п’ятницю.
Девід Торнлі

Це більше випробування самої ПРНГ; тест обмежувальних механізмів (механізмів), як це було структуровано вище, завжди провалив би такий тест, оскільки йому надається ретельно невипадковий набір даних. Якщо припустити, що механізм обмеження не намагається «замовляти» випадкові дані, я б назвав тестування зовнішніх, що є тестуванням одиниці, не повинно робити.
KeithS

Відповіді:


17

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

Якщо це те, що ви шукаєте, тоді знущайтеся з рандомізера, щоб зробити його детермінованим у царинах тесту.

Я, як правило, маю об’єкти для всіх видів недетермінованих або непередбачуваних (на момент написання тесту) даних, включаючи GUID-генератори та DateTime.Now.

Редагувати, з коментарів: Ви повинні знущатися з PRNG (цей термін уникнув мене минулої ночі) на найнижчому можливому рівні - тобто. коли він генерує масив байтів, не після того, як ви перетворите їх на Int64. Або навіть на обох рівнях, так що ви можете протестувати перетворення в масив Int64 робіт за призначенням, а потім перевірити окремо, що ваше перетворення в масив DateTimes працює за призначенням. Як сказав Джонатан, ви могли просто зробити це, давши йому набір насіння, або можете дати йому масив байтів, щоб повернутися.

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

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


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

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

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

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

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

23

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

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

Це звучить безглуздо, але так це роблять військові (або це, або вони використовують "випадкову таблицю", яка насправді не є випадковою)


Саме так: якщо ви не можете перевірити елемент алгоритму, абстрагуйте його та
знущайтеся

Я не вказав детермінованого значення насіння; натомість я повністю видалив "випадковий" елемент, тому мені навіть не довелося покладатися на конкретний алгоритм PRNG. Я зміг перевірити, що, враховуючи великий діапазон рівномірно розподілених чисел, алгоритм, з яким я пішов, може обмежувати їх меншим діапазоном, не вводячи зміщення. Сам PRNG повинен бути адекватно випробуваний тим, хто його розробив (я використовую RNGCryptoServiceProvider).
KeithS

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

Це не так вже й дурно - це той самий метод, який Google використовує для відтворення ін'єкцій на відмову в тестах Spanner відповідно до їхніх робіт :)
Акшат Махаджан

6

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

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

Стаття у Вікіпедії http://en.wikipedia.org/wiki/Randomness_tests і його наступні до посилання мають більше інформації.


Навіть посередні PRNG не показуватимуть жодних малюнків у жодних статистичних тестах. Для хороших PRNG відрізнити їх від реальних випадкових чисел практично неможливо.
CodesInChaos

4

Я маю для вас дві відповіді.

=== ПЕРШИЙ ВІДПОВІДЬ ===

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

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

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

Тож замість того, щоб зануритися у важку проблему, я подумав собі: у чому цінність цього? І відповідь була невтішною. Ваша помилка вже вирішена, і ви будете старанно ставитися до цієї проблеми в майбутньому. Зовнішні обставини не можуть викликати проблему, лише змінюють ваш алгоритм. ТІЛЬКИ причиною вирішення цієї цікавої проблеми було задоволення практики TDD (Test Driven Design). Якщо я дізнався одну річ, це те, що сліпо дотримуватися будь-якої практики, коли вона не є цінною, викликає проблеми. Моя пропозиція така: Просто не пишіть на це тест, а рухайтесь далі.


=== ДРУГИЙ ВІДПОВІДЬ ===

Ух ... яка класна проблема!

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

  1. Можна застосувати грубу силу. Просто запустіть алгоритм цілий ряд разів з реальним RNG як вхід. Перевірте результати виводу, щоб побачити, чи розподілені вони рівномірно. Ваш тест повинен бути невдалим, якщо розподіл варіюється від ідеально рівномірного більш ніж на певну межу, а для того, щоб уникнути проблем, порог не може бути встановлений ТАК низьким. Це означає, що вам знадобиться ВЕЛИЧЕЗНАЯ кількість запусків, щоб переконатися, що ймовірність помилкового позитивного (тест-помилка випадковою випадковістю) дуже мала (добре <1% для середньої бази коду; ще менше для велика база коду).

  2. Розгляньте свій алгоритм як функцію, яка приймає конкатенацію всіх виходів RNG як вхід, а потім виробляє час перевірки як вихід. Якщо ви знаєте, що ця функція є кусочно безперервною, то є спосіб перевірити своє майно. Замініть RNG макетним RNG і запустіть алгоритм багато разів, виробляючи рівномірно розподілений вихід RNG. Отже, якщо для вашого коду потрібні 2 виклики RNG, кожен у діапазоні [0..1], ви, можливо, будете пробувати алгоритм тестування 100 разів, повертаючи значення [(0,0,0,0), (0,0,0,1), (0,0, 0,2), ... (0,0,0,9), (0,1,0,0), (0,1,0,1), ... (0,9,0,9)]. Тоді ви можете перевірити, чи результат (100) запускається (приблизно) рівномірно розподілений у межах дозволеного діапазону.

  3. Якщо вам дійсно потрібно перевірити алгоритм надійно і ви не можете робити припущення щодо алгоритму АБО запускати велику кількість разів, ви все одно можете атакувати проблему, але вам можуть знадобитися деякі обмеження щодо того, як ви запрограмуєте алгоритм . Ознайомтеся з прикладом PyPy та їх об'єктного простору . Ви можете створити об'єктний простір, який замість того, щоб насправді виконати алгоритм, замість цього просто обчислив форму розподілу виводу (якщо припустити, що вхід RNG є рівномірним). Звичайно, для цього потрібно створити такий інструмент і щоб ваш алгоритм був побудований в PyPy або іншому інструменті, де легко зробити кардинальні модифікації компілятора і використовувати його для аналізу коду.


3

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

Ви не хочете, щоб ваші одиничні тести не оминали уваги, наприклад, помилки, що відбуваються один за одним, коли Random.nextInt (1000) повертає 0 або 999.


3

Ви можете поглянути на Sevcikova et al: "Автоматизоване тестування стохастичних систем: статистично обґрунтований підхід" ( PDF ).

Методика реалізована в різних тестових випадках для імітаційної платформи UrbanSim .


Це хороші речі.
KeithS

2

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

Мій підхід був би таким:

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

Кожен з цих тестів є статистичним та вимагає великої кількості вибіркових балів, щоб уникнути помилкових позитивних результатів та помилкових негативів з високим ступенем впевненості.

Щодо характеру алгоритму перетворення / обмеження:

Дано: метод для генерації псевдовипадкових значень p, де 0 <= p <= M

Необхідність: вивести y в (можливо переривчастому) діапазоні 0 <= y <= N <= M

Алгоритм:

  1. обчислити r = floor(M / N), тобто кількість повних діапазонів виходу, що вписуються у вхідний діапазон.
  2. обчислити максимальне прийнятне значення для p :p_max = r * N
  3. НЕ генерувати значення для р до величини , меншої , ніж або дорівнює p_maxзнайдений
  4. розрахувати y = p / r
  5. якщо y прийнятно, поверніть його, інакше повторіть з кроком 3

Головне - відкинути неприйнятні значення, а не складати нерівномірно.

у псевдокоді:

# assume prng generates non-negative values
def randomInRange(min, max, prng):
    range = max - min
    factor = prng.max / range

    do:
        value = prng()
    while value > range * factor
    return (value / factor) + min

def constrainedRandom(constraint, prng):
    do:
        value = randomInRange(constraint.min, constraint.max, prng)
    while not constraint.is_acceptable(value)

1

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


1

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


Але якщо це справді випадково, то зрідка якийсь графік взагалі не буде використовуватися; а іноді деякий графік буде використовуватися більше 20 разів. Я не знаю, як ви маєте намір перевірити, що кожен графік використовується "близько 10 разів", але при будь-якій умові ви тестуєте тут, у вас буде тест, який іноді не працює, коли програма працює на специфікацію.
Давуд каже, що поверніть Моніку

@DawoodibnKareem з достатнім розміром вибірки (і розумними обмеженнями на рівномірність) ви можете зменшити шанс тесту не вдатися до 1 у мільярдах. І взагалі така статистика є експоненціальною з n, тому для отримання цих цифр потрібно менше, ніж ви розраховуєте.
mbrig

1

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

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


2
Ви можете абсолютно визначити випадковість за допомогою автоматизованого тестування. Ви просто генеруєте достатньо велику кількість зразків і застосовуєте стандартні тести на випадковість, щоб виявити зміщення в системі. Це досить стандартна вправа з програмування недоградів.
Франк Щебра

0

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


0

Цей вигляд здається ідеальним для тестування на основі властивостей .

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

Ось (тупий, невдалий) приклад тестування sumфункції:

@given(lists(floats()))
def test_sum(alist):
    result = sum(alist)
    assert isinstance(result, float)
    assert result > 0
  • тестова рамка буде генерувати один список за один раз
  • Зміст списку буде цифрами з плаваючою комою
  • sum називається і властивості результату перевірені
  • результат завжди плаває
  • результат позитивний

Цей тест знайде купу "помилок" sum(коментуйте, якщо ви змогли самі здогадатися про все це):

  • sum([]) is 0 (int, не поплавок)
  • sum([-0.9]) є негативним
  • sum([0.0]) не є суто позитивним
  • sum([..., nan]) is nan що не є позитивним

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

У випадку з ОП валідовані властивості будуть складнішими: інспекція типу А, інспекція типу А тричі на тиждень, час огляду В завжди о 12 вечора, огляд типу С з 9 до 9, [заданий графік - на тиждень] перевірки типів A, B, C всі присутні тощо.

Найвідоміша бібліотека - QuickCheck для Haskell, перелік таких бібліотек іншими мовами див. На сторінці Вікіпедії нижче:

https://en.wikipedia.org/wiki/QuickCheck

Гіпотеза (для Python) добре написала інформацію про такий вид тестування:

https://hypothesis.works/articles/what-is-property-based-testing/


-1

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

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


-1

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

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

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


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