Це був звичайний будній день, я почав писати черговий звичайний тест, але при написанні назви тесту щось пішло не так:
Приблизно на середині я почав усвідомлювати що відбувається щось не те, але я не зупинився і дописав назву тесту до кінця. Колеги не оцінили таке довжелезне назву тесту, але мені воно подобається. Зараз використовується щось на зразок testCorrectChangeCustomerRedirectUrl. а деталі того що тест тестує приховані всередині тесту.
Я не можу зрозуміти це лвлап, або я втомився під вечір.
Питання: Які критерії вибору хорошого назви тесту?
UPD: Будь ласка, приведіть назви тестів з ваших проектів.
Знову закривальщікі понабежалі. Панове, ну давайте включати голову. Якщо вам питання здається незрозумілим тільки тому, що ви не в темі, це не означає, що це даремний питання. До того ж, як я вже писав, в програмуванні є ряд областей, в яких в принципі неможливо дати однозначну відповідь, але це важливі області. Найбільш очевидний приклад - проектування ПО. - andreycha 2 дек '15 о 17:13
Назва для тестів не треба вибирати гарне. Назва потрібно вибирати функціональне і по можливості короткий - щоб по мінімальній кількості символів було зрозуміло, що і на яких умовах він тестує, а також щоб можна було відрізнити конкретний тест від тисяч інших.
Поширені шаблони найменування такі:
- TestClass_TestMethod_ConditionAndExpectedResult (для юніт-тестів)
- TestMethod_Condition_ExpectedResult (для юніт-тестів)
- ConditionAndExpectedResult (для інтеграційних тестів, які тестують цілі блоки функціональності через деяку вхідну точку)
При цьому якщо у вас в частинах Condition / ExpectedResult занадто багато різних умов / результатів, об'єднаних по "І", то вам потрібно переглянути або головну мету тесту (що він тестує), або головна умова, яке відрізняє тест від інших, і розбити його на кілька окремих тестів.
Я виступаю насамперед за те, що назва має бути інформативною, але при цьому настільки коротким, наскільки можливо. Тут точно так само як і з усім іншим кодом - чим менше коду, тим менше часу потрібно щоб його зрозуміти, тим простіше розбиратися з ним. Судячи з вашого назвою, це не простий маленький юніт-тест, а щось більше, тому зовсім коротким назвою тут не обійтися. У вашому випадку, якщо тест клас у вам вже для Customer. я б обмежився назвою SelectLastTransactionProcess_CreateRecurringProfile_RedirectUrlShouldContainConfirmation. У вашому назві багато повторень слів: transaction, url, recurring profile і т.д. Вони надлишкові.
Між довжиною і інформативністю назви безумовно повинен бути баланс. Досить, щоб назви були зрозумілі розробникам всередині проекту, які вже володіють контекстом і здатні зрозуміти, про що цей тест. І якщо для цього потрібно довжину назви зробити не 30 символів, а 40, зробіть. Але не потрібно прагнути до того, щоб назви були зрозумілі кожному зустрічному, попутно розповідаючи про всю систему, оскільки це призводить до таких ось кілометровим назвам, в яких складно розібратися. Подивіться на свій звичайний код, подивіться на назви методів. Адже ви не описуєте в назві методу кожну його деталь, кожен рядок? Ви описуєте щось головне, а інше заховано всередині. Точно так само і з тестовими методами.
Хто-небудь може ще розповісти що робити з назвами інтеграційних тестів,> де 100500 різних умов?
Наприклад білінг, приймає на вхід 5-10 видів прайс-листів і в залежності від їх комбінацій і комбінацій їх параметрів (теж приблизно 5-10) повинен видавати різні суми на вихід. Контейнер відправляється в білінг займає
30 рядків. Якщо в стилі поста називати тести то це буде testPayoutDebtorToGateOntopGateTransactionAmountGateToCreditorFlatPayinDebtorTo GateOntopTotalTransactionAmountGateToCreditorOntopTransactionAmount. причому не з повним контекстом.
Потрібно розрахувати підсумкову суму виходячи з того що прайс-листи могли бути налаштовані таким чином: Тип комісії прайс-листа типу основної угоди - inside / total, комісія 0.1% + 0.5руб cверху, розрахунок вести від обороту. Другий комісійний прайс-лист ontop / gate. ще в такому ж стилі раз 10 і пара неявних залежностей прайс-листів один від одного.
Я б сказав, що вам потрібен параметричний тест. Це такий тест, який одним кодом тестує різні набори даних. Наприклад, в термінах тестового фреймворка xUnit параметричний тест методу Calculator.Add () може виглядати так:
З параметричними тестами вам не потрібно перераховувати всі умови в назвах - назва містить тільки суть тесту, що саме тестується, конкретний кейс. У вашому випадку це щось на зразок РасчетІтоговойСтоімості_ПоОсновнойІДополнітельнойСделке (на англійську самі перекладете, вам область зрозуміліше). А виглядати ваш тестовий метод може так (при цьому в xUnit тестові дані можна підставляти з властивості і навіть з окремого об'єкта):
Я думаю у багатьох тестових фреймворка є можливість створення параметричних тестів. Спосіб завдання параметрів може відрізнятися, але суть залишиться та ж: дані виносяться в секцію з даними, в назві тесту залишається тільки назва кейса.
Додаткову інформацію про параметричні тестах можливої знайти в блозі Сергія Теплякова.
Перед найпершим "модульним тестом"
Потрібно домовитися з самим собою або командою про те, що вважати "модульним тестом"?
Наприклад в книзі Роя (див. В розділ "рекомендована література") дається чітке визначення "unit" і досить зручне для програміста.
Без чіткого і впевненого розуміння, що таке "unit" писати модульні тести настійно не рекомендую. Навіть якщо Ваше розуміння, що таке модульний тест відрізняються від того що говорить Рой все одно воно повинно бути уніфікованим і стабільним для ВАС або Вашої команди. Що вимагає певної домовленості.
Розуміння "Модульний тест" від Роя:
Рой, на мій погляд, пропонує найзручнішу і практичну схему іменування:
Де: unit-of-work - це якраз те що Ви розумієте під 'unit' або про що домовилися з командою. scenario - це конкретна дія над тестованим об'єктом expected-results-or-behaviour - це Ваші очікування щодо того, що має статися
Відразу видно, що перевіряється парсер json-документа на файлі з неприпустимим файловим розширенням і тому має бути кинуто InvalidLogFileException.
Тут видно, що при оптимізації байт-коду буде повертатися -1 в разі якщо зустрінеться невідома інструкція
Написання модульних тестів зі складними умовами
- Це зовсім інше питання
- Якщо коротко, то в одному модульному тесті ОДНА перевірка. Якщо умова складно, значить там кілька під умов і значить треба Взяти і розбити на кілька перевірок - модульних тестів
- У будь-якому тесті не повинно бути умовною логіки. Якщо в production-коді є дві гілки, одна if-частина, інша else-частина. Значить пишеться ДВА модульних тесту.
і т.д. і т.п. Але все це тема іншого обговорення
Що неправильно, на мій погляд, в вашому тесті:
Настійно рекомендую скачати, а краще купити книгу Art Of Unit Testing. Навіть якщо раптом не пишіть на C #. Знання корисні!
Дуже довга назва тесту можна вважати діагностичним сигналом. Такі сигнали можна розглядати з різних точок зору, але в будь-якому випадку, як правило вони свідчать про наявність або можливе виникнення проблеми. Мінімально можливі варіанти:
- В тестованому коді реалізується неправильна архітектура
- Це не модульний, а можливо інтеграційний тест
- Неправильна реалізація власне тестового модуля
- Розробник не виспався і (або) вживав будь-які трансформатори свідомості в надмірних кількостях
Раз вже ви навели приклад, то давайте на нього і подивимося. Для початку я спробую витягти з англійської назви вашого тесту російський сенс:
"Упевнитися в тому, що URL, на який перенаправляється клієнт після обробки останньої транзакції, повинен містити URL відповідний профілю повернення на сторінку, якщо тип транзакції реалізує профіль повернення."
Давайте припустимо як може виглядати власне тіло такого тесту:
У першому наближенні всі начебто коротко, симпатично і читабельно. Давайте припустимо як може виглядати тест, який засвідчується в зворотному:
Природно ми припускаємо що вся підготовча перед тестом робота винесена з власне тестів:
Як я називаю тести? Приблизно так (з робочого проекту):
Ім'я тестового класу посилається на найменування тестованого класу в коді. А імена тестових методів позначають імена тестованих методів в тестовому класі і намагаються коротко повідомити про умови тестування кожного конкретного методу.
Правила іменування - це в деякому роді Дао або Кунг-Фу. Необхідно дотримуватися балансу. Якщо ім'я тестового методу довше, ніж сам код, що міститься в тестовому методі, то розробнику, який рефактору метод, може бути простіше і швидше прочитати і зрозуміти код, ніж продиратися спочатку через літературні перипетії назви англійською мовою.
відповідь даний 18 дек '15 в 4:21
1. Імена тестів повинні бути рядками
Почати треба з того, що ім'я тесту - це ім'я функції. На відміну від функцій, ми не викликаємо тести з коду, ім'я тесту потрібно тільки для того щоб один раз описати що він тестує, щоб це опис потім записалося в лог з результатами тестування.
З цього в деяких тестових фреймворк імена тестів - це звичайні рядки.
Наприклад в Catch (C ++)
Або Mocha (JS та інші * script)
Цікавим винятком є мови де ідентифікатори можуть мати будь-які символи, як наприклад в F #
2. Не треба повторюватися (принцип DRY)
Якщо тестовий фреймворк дозволяє групувати тести, то це треба використовувати. Замість тестів Чтото_КогдаЕто_ВотЕто. Чтото_КогдаДругое_ВотЕто і Чтото_КогдаДругое_ЕщеІЕто тести можна згрупувати.
Приклад на Mocha / JS:
Також часто немає сенсу повторювати код тесту в назві тесту. Треба описувати що перевіряє тест, а не як він це робить. З цього замість StartButton_WhenPressed_BecomesDisabled можна написати
Як правило тест включає в себе початкові умови (GIVEN / WHEN частина) і результати (THEN частина). Якщо для якихось початкових умов тест один, то не навіщо писати в назві то що перевіряється в THEN частині. Це видно з коду, і якщо тест провалиться, то текст провалену перевірки буде написаний в балці.
Приклади імен тестів
Імена тестів залежать від тестового фреймворка і від рівня тесту (юніт / інтеграційний / регресійний).
Регресивні тести на Mocha / JS
Юніт-тести на Catch / С ++, коли в Catch використовувалися імена тестів розділені /
Більш нові тести Catch / C ++, з тегами
Тести на gtest / C ++ (з коду Chromium)
Тести на Golang
Якщо ім'я тесту не можна задати рядком, то одночасне використання CamelCase і нижнього підкреслення дає досить непогано підвищує читабельність. Наприклад замість TEST_CASE ( "some unit")
Ваш же варіант іменування сильно схожий на яку використовували кілька століть назад вітіювату манеру давати назви книг, коли в назві описується чи не весь сюжет книги. Наприклад, повна назва Робінзона Крузо звучить страхітливо-довго:
"Життя, незвичайні і дивовижні пригоди Робінзона Крузо, моряка з Йорка, який прожив 28 років в повній самоті на пустельному острові біля берегів Америки поблизу гирл річки Оріноко, куди він був викинутий корабельною аварією, під час якого весь екіпаж корабля крім нього загинув, з викладом його несподіваного звільнення піратами; написані ним самим "
відповідь дан 2 дек '15 о 15:03
@ Onedev.Link ви займаєтеся перфекционизмом в гіршому його прояві. Лізти в тести для визначення поломки рано чи пізно доведеться, і чогось прям вже жахливого в цьому немає (в кінці кінців швидше за все ви не напишете ідеальний все покриває тест відразу), а застаріти у вас може і назвою методу. Навіть якщо вам настільки принципово опис логіки тесту саме в його назві (сумнівна необхідність), і при цьому назви виходять дивовижно довгими, то вам варто подумати про розбиття вашого тестового класу на кілька нових, кожен з яких буде тестувати щось більш конкретне - DreamChild 18 дек '15 в 9:43
Я вважаю що назва модульного тесту повинно бути ємним. Особливо якщо тест пишеться в BDD стилі. Особливо якщо ваш фреймворк для тестування вміє це розкладати в читабельні звіти.
На мій погляд ємні назви (приклад):
- testShouldAlwaysPreparePathsAndConvertThemInToArray
- testShouldRegisterNamespacesForMultipleDirectories
- testShouldThrowExceptionIfDbIsMissingOrInvalid
- testShouldSetOptionsWhenConfigPassedToTheConstructor
У будь-якому пристойному фреймворку для тестування такі імена методів трансформуються в щось подібне (приклад):