Чи погана практика примусово виконувати наказ про виконання одиничних тестів?


84

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

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

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

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

редагувати: питання в тому, як структурувати тести, коли один тест - це налаштування іншого тесту? оскільки "попередній" тест не є налаштуванням, а тестує код, який виконує налаштування.



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

9
Перша відповідь мене тут збентежила, оскільки у своєму заголовку ви сказали: "Це погана практика?" і в резюме вашого запитання ви написали "це нормально?" Хто відповість "так" чи "ні", не збирається плутати одного з них!
Ліат

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

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

Відповіді:


236

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

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

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


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

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

10
Проблема, швидше за все, в тому, що Джуніт має "одиницю" у своєму імені.
Thorbjørn Ravn Andersen

7
@ ThorbjørnRavnAndersen Рівно. Люди, природно, пишуть інтеграційні тести, а не одиничні тести, тому що інтеграційні тести є і набагато кориснішими, і набагато менш складними, ніж "реальні" одиничні тести. Але оскільки популярні рамки тестування отримали назву від концепції одиничних тестів, цей термін був кооптований і означав «будь-яке автоматизоване тестування» в сучасній мові.
Мейсон Уілер

1
@MasonWheeler Або навіть кооптований не технічними менеджерами, щоб мати на увазі тестування прийняття.
TKK

32

Так, це погана практика.

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

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

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


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

8
@autophage: Безумовно, згоден з цим. Насправді я настільки погоджуюся з цим, що регулярно потрапляю в одну пастку, незважаючи на те, що це пастка 😂
Гонки легкості на орбіті

16

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

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

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

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


7

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

Блок тестів часто плутають з "автоматизованим тестуванням" або "автоматизованим тестуванням програмістом".

Тести одиниць повинні бути невеликими, незалежними та швидкими.

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

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


6

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

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

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

напр

Якщо у вас є:

class Queries {

    int GetTheNumber() {
        var dataModule = new Submodule1();
        var data = dataModule.GetData();
        return ... run some query on data
    }
}

Натомість віддайте перевагу:

interface DataModule {
    Data GetData();
}


class Queries {

    IDataModule _dataModule;

    ctor(IDataModule dataModule) {
       _dataModule = dataModule;
    }

    int GetTheNumber() {
        var data = _dataModule.GetData();
        return ... run some query on data
    }
}

Це знімає залежність від запитів на вашому джерелі даних і дозволяє налаштувати легко повторювані тестові одиниці для конкретних сценаріїв.


6

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


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

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

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

@MikeHoller Тоді вони не є одиничними тестами. Справді не повинно бути плутанини щодо того, що таке одиничні тести .
Роббі Ді

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