Як застосувати TDD для читання / запису функцій?


10

Це здається проблемою з куркою та яйцями.

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

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

Редагувати:

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

Це не так, як додавати / видаляти, як зазначає @snowman. Хочу знати, що вміст, який я написав, є правильним, але для цього потрібна перевірена функція читання. Коли я читаю, я хочу бути впевненим, що мій прочитаний правильно створив об’єкт, рівний тому, що було написано; але для цього потрібна добре перевірена функція запису.


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


1
Хоча тісно пов'язаний, я не думаю, що це дублікат цього конкретного питання. Ціль дупа говорить про додавання / видалення, ця - читання / запис. Різниця полягає в тому, що залежність від читання / запису, ймовірно, покладається на вміст об'єктів, які читаються / записуються, тоді як простий тест додавання / видалення, ймовірно, буде набагато простішим: чи існує об'єкт чи ні.

2
Можна перевірити базу даних з даними та взагалі без функції запису, щоб перевірити функцію читання.
JeffO

Відповіді:


7

Почніть з функції читання.

  • У тестовій установці : створіть базу даних та додайте тестові дані. або через сценарії міграції, або з резервної копії. Оскільки це не ваш код, він не вимагає тесту в TDD

  • У тесті : інстанціюйте своє сховище, вкажіть на свій тестовий db та зателефонуйте методу Read. Перевірте, чи повертаються дані тесту.

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


Я думаю, ви можете створити БД в пам'яті, щоб прискорити роботу, але це може бути занадто складно. Чому б не використовувати макети замість них в одиничних тестах?
BЈоviћ

1
Тут трохи захисника диявола, але як ви перевірите, чи правильно створена база даних? Як заявили в ОП, курка і яйце.
user949300

1
@Ewan - Я абсолютно згоден, що ви не повинні перевіряти їх код DB. Але як ви знаєте, що ваш код установки БД не забув INSERT десь або поставив неправильне значення у стовпчик?
user949300

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

1
Quis зберігач ipsos зберігання? Або "Хто тестує тести?" :-) Я згоден з вами, що в пуристичному світі TDD це було б жахливо виснажливим і схильним до помилок (особливо, якби це була складна структура декількох таблиць з 8 ПРИЄДНАННЯМ) спосіб це зробити. Отримано.
user949300

6

Я часто просто пишу, а потім читаю. наприклад (псевдокод)

Foo foo1 = setup some object to write
File tempfile = create a tempfile, possibly in memory 
writeFoo(foo1, tempfile) 
Foo foo2 = readFoo(tempfile) 
assertEquals(foo1, foo2); 
clean-up goes here

Додано пізніше

Окрім того, що це рішення є "примматичним" та "досить хорошим", можна стверджувати, що інші рішення перевіряють неправильну річ . Тестування, чи збігаються рядки або SQL заяви, не є страшною ідеєю, я це робив сам, але це тестування побічного ефекту і є крихким. Що робити, якщо ви зміните великі літери, додасте поле або оновите номер версії у своїх даних? Що робити, якщо ваш драйвер SQL перемикає порядок викликів для підвищення ефективності, або ваш оновлений серійний інструмент XML додає додаткового місця або змінить версію схеми?

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


1
Тому що це на 90% дійсно щільний псевдокод? Не впевнений. Може, виділити текст і зробити код менш галасливим?
RubberDuck

1
Так @Ewan. Завзяття на це нахмурився, однак прагматичний програміст сказав би «досить добре» і піде далі.
RubberDuck

1
я начебто прочитав питання як .. «припускаючи , я слідувати TDD як фанатика ...»
Ewan

1
моє тлумачення OP та TDD полягає в тому, що ваш тест повинен бути написаний спочатку, а не використовувати як читання, так і запис, якщо тільки тест не перевіряється в іншому місці.
Еван

2
ви тестуєте, "читання має повернути те, що я пишу", але вимоги "читання повинні повертати дані з db" і "писати слід записувати дані в db"
Еван

4

Не варто. Не перевіряйте модуль вводу / виводу. Це марна трата часу.

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

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

Ваш код повинен відокремлюватися:

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

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

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


Ось кілька прикладів:

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

2

Я не знаю, це стандартна практика чи ні, але це добре працює для мене.

У моїх реалізаціях методу читання, що не читаються, я використовую власні специфічні типи toString()та fromString()методи як деталі реалізації.

Їх можна легко перевірити ізольовано:

 assertEquals("<xml><car type='porsche'>....", new Car("porsche").toString());

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

До речі: чи є щось не так у тому, щоб один тест, який тест читали / писали разом?


Він може не відчувати себе добре чи "чистим", але це прагматичне рішення.
RubberDuck

Мені теж подобається ідея тестування читання та запису разом. Ваш toString () - приємний прагматичний компроміс.
user949300

1

Відомі дані повинні бути відформатовані відомим способом. Найпростіший спосіб здійснити це - використовувати постійний рядок і порівняти результат, як описано @ k3b.

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

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


1

Використовуйте ін'єкційну залежність та глузування.

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

Отже, коли у вас є метод / клас, який повинен щось робити з базою даних, нехай він не отримує це з'єднання з базою даних самостійно. Змініть його так, щоб об’єкт, який представляє з'єднання з базою даних, передається йому.

У своєму виробничому коді передайте власне об’єкт бази даних.

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

Таким чином ви можете протестувати рівень абстракції вашої бази даних, навіть не потребуючи фактичної бази даних.


Адвокат диявола: звідки ви знаєте, які заяви SQL він "повинен отримувати"? Що робити, якщо драйвер БД оптимізує порядок із того, що з’являється в коді?
user949300

@ user949300 Об'єкт макету бази даних зазвичай замінює драйвер бази даних.
Філіп

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

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

так. Я кажу, що немає одиничної точки тестування сховища DB. інтеграційний тест є єдиним , що варто робити
Ewan

0

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

NHibernate пропонує тестування специфікації стійкості . Він може бути налаштований на роботу проти сховища пам'яті для швидких тестів на одиницю.

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

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