Блок тестування статично набраного функціонального коду


15

Я хотів запитати вас, у яких випадках має сенс випробувати тест на статично набраний функціональний код, написаний у haskell, scala, ocaml, nemerle, f # або haXe (останнє - це те, що мене справді цікавить, але я хотів скористайтеся знаннями великих громад).

Я прошу це тому, що з мого розуміння:

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

  • Більш очевидний аспект одиничних тестів полягає у виявленні помилок, які неможливо виявити за допомогою статичного аналізу. Зважаючи на те, що безпечний функціональний код типу є хорошим інструментом для кодування, надзвичайно близького до того, що розуміє ваш статичний аналізатор, здається, що ви можете перенести багато безпеки на статичний аналіз. Однак просту помилку, як-от використання xзамість y(обидві - координати) у вашому коді, неможливо покрити. ОТОХ така помилка могла виникнути і під час написання тестового коду, тому я не впевнений, чи варто докладати зусиль.

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

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

Коли ви використовуєте такий стиль програмування, для яких розширень ви використовуєте одиничні тести та чому (яку якість ви сподіваєтесь отримати для свого коду)? Або навпаки: чи є у вас критерії, за якими ви можете кваліфікувати одиницю статично набраного функціонального коду як охопленого статичним аналізатором і, отже, без необхідності покриття тестових одиниць?


4
До речі, якщо ви ще не пробували QuickCheck , ви обов'язково повинні.
Джон Перді

scalacheck.org - еквівалент Scala
V-Lamp

Відповіді:


8

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

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

Однак просту помилку, наприклад використання x замість y (обидві координати) у вашому коді, не може бути покрито. Однак така помилка може виникнути і під час написання тестового коду, тому я не впевнений, чи варто того докласти зусиль.

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

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

В інженерії більшість систем безпеки покладаються на надмірність. Наприклад, два перерви в машині, надлишковий парашут для дайвера в небі тощо. Ця ж ідея стосується і тестів на одиниці. Звичайно, наявність більшої кількості коду для зміни, коли зміни вимог можуть бути недоліком. Тому особливо в одиничних тестах важливо зберегти їх СУХОМО (дотримуйтесь принципу "Не повторюй себе"). Статично набраною мовою вам, можливо, доведеться писати трохи менші одиничні тести, ніж у слабко набраній мові. Особливо "формальні" тести можуть не знадобитися - що добре, оскільки це дає більше часу для роботи над важливими одиничними тестами, які перевіряють істотні речі. І не думайте лише тому, що вам статичні типи, вам не потрібні одиничні тести, є ще достатньо місця для введення помилок під час рефакторингу.


5

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

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

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

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

Часто специфікація (або її частина) функції може бути виражена як властивості, що відносять повернене значення до аргументів. У цьому випадку використання QuickCheck (для Haskell) або ScalaCheck (для Scala) може дозволити вам записати ці властивості як вирази мови та перевірити, чи вони містять випадкові введення.


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

1

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

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

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


0

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

prop_encode a = (decode . encode $ a) == a

Ви можете примусити prop_encodeстатичний тип.

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