Створення одиничних тестів на рівні CRUD програми, як я можу зробити тести незалежними?


14

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

Для методу add я в основному повинен створити фіктивний об'єкт і додати його, після того, як тест буде успішним, я повинен видалити манекенний об’єкт.

А для тесту на видалення я, очевидно, повинен створити фіктивний об’єкт, щоб я міг його видалити.

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

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

Як розглядаються такі випадки?


Ви також можете поглянути на це питання: programmers.stackexchange.com/questions/115455/…
Guven

Відповіді:


13

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

Прекрасний спосіб зробити це - запустити тести на базі даних пам'яті.

У своєму тесті на додавання створіть порожню базу даних, додайте об'єкт і запевняйте, що він дійсно доданий.

У своєму тесті на видалення створіть базу даних з об’єктом, який ви будете видаляти вже в ній. Видаліть об’єкт і запевняйте, що він був видалений.

Здуйте базу даних у вашому зірваному коді.


База даних в пам'яті просто швидка (в пам'яті) і проста (в процесі роботи). Це можна зробити в будь-якому сховищі даних.
Пол Дрейпер

3

Використовуйте транзакції.

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

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


Любіть цю ідею. Сьогодні це реалізується з великим ефектом.
pimbrouwers

3

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


2

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

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

  • Кожен тест повинен бути автономним і не впливати на інші тести
  • Кожен одиничний тест повинен перевірити одне, і лише одне
  • Одиничні тести не повинні потрапляти в базу даних

і так далі. І все це правильно, залежно від того, як ви визначаєте "одиничний тест" .

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

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

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

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

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


Так ви кажете, що не можете вилучити тестове видалення з бази даних?
ChrisF

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

Але справа в тому, що деякі люди можуть по-різному визначати "тест одиниці", тому ми повинні бути обережними, застосовуючи вказівки "тестування одиниці", оскільки вказівки можуть не застосовуватися так, як ми думаємо.
Ерік Кінг

1

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

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


1

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

Так?

... це не суперечить вказівкам тестування одиниць?

Ні.

Як розглядаються такі випадки?

Декілька тестів можуть бути незалежними, і всі провалитися через одну і ту ж помилку. Це насправді нормально. Багато тестів можуть - опосередковано - перевірити деякі загальні функції. І все не вдається, коли загальна функціональність порушена. Нічого поганого в цьому немає.

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


1

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

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


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