Тестування блоку - початок роботи


14

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

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

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

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

Заздалегідь спасибі.

Відповіді:


7

Мої 0,02 $ ... це трохи суб'єктивно, тому візьміть із собою зерно солі, але, сподіваємось, це змусить вас задуматися та / або розпалити деякий діалог:

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

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

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

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

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


5
  1. Спробуйте спочатку написати свої тести. Таким чином, у вас буде міцна база для поведінки вашого коду, і ваш тест стане договором на необхідну поведінку вашого коду. Таким чином, зміна коду для проходження тесту стає "зміною коду для виконання запропонованого тестом контракту" замість "зміни коду для проходження тесту".
  2. Ну, будьте уважні до різниці між заглушками та макетами. Будь-яка зміна коду не впливає на характерну поведінку заглушок, але не знущається. Почнемо з визначення макету:

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

    - Мистецтво одиничного тестування

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


1

Поставити гарне запитання ні в якому разі не глупо.

Я вирішу ваші запитання.

  1. Мета одиничного тестування - не тестування коду, який ви вже написали . Він не має поняття часу. Тільки в TDD ви повинні пройти тестування спочатку, але це не застосовується строго до будь-якого виду тестування одиниць. Сенс у тому, щоб мати можливість автоматично та ефективно перевірити свою програму на рівні класу. Ви робите те, що вам потрібно зробити, щоб потрапити туди, навіть якщо це означає зміну коду. І дозвольте сказати вам секрет - це часто означає це.
  2. Коли ви пишете тест, у вас є два основні варіанти, щоб переконатися, що ваш тест правильний:

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

    Ось приклад:

    TEST(MyTest, TwoPlusTwoIsFour) {
        ASSERT_EQ(4, 2+2);
    }
    
    TEST(MyTest, TwoPlusThreeIsntFour) {
        ASSERT_NE(4, 2+3);
    }
    

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

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


1

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

1) Знайдіть фрагмент коду, який було написано / скопійовано не один раз. Навіть якщо це просто string fullName = firstName + " " + lastName. Розбийте це на метод, наприклад:

private static string GetFullName (firstName, lastName)
{
    return firstName + " " + lastName;
}

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

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


0

Якийсь розумний хлопець сказав "Якщо це важко перевірити, це, мабуть, поганий код". Ось чому не погано переписувати код, щоб мати можливість його тестувати. Код із зовнішніми залежностями ДУЖЕ ДУЖЕ перевірити, він представляє ризик для коду, і його слід уникати, де це можливо, і зосереджуватись на інтеграції конкретних областей вашого коду, fx. класи фасаду / шлюзу. Це значно спростить зміну зовнішнього компонента.

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