Модульное тестирование статически типизированного функционального кода


15

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

Я спрашиваю об этом, потому что из моего понимания:

  • Один из аспектов модульных тестов - иметь спецификации в работоспособном виде. Однако при использовании декларативного стиля, который непосредственно отображает формализованные спецификации для семантики языка, это даже на самом деле можно выразить спецификации в работоспособной форме в отдельном пути, который добавляет значение?

  • Более очевидным аспектом модульных тестов является отслеживание ошибок, которые невозможно выявить с помощью статического анализа. Учитывая, что функционально безопасный код типа является хорошим инструментом для кодирования, очень близкого к тому, что понимает ваш статический анализатор, может показаться, что вы можете сместить большую безопасность в сторону статического анализа. Однако простая ошибка, такая как использование xвместо y(обе являются координатами) в вашем коде, не может быть покрыта. OTOH такая ошибка также может возникнуть при написании тестового кода, поэтому я не уверен, стоит ли это усилий.

  • Модульные тесты вводят избыточность, что означает, что при изменении требований код, реализующий их, и тесты, охватывающие этот код, должны быть изменены. Эти накладные расходы, конечно, постоянны, так что можно утверждать, что это не имеет значения. На самом деле, в таких языках, как Ruby, это на самом деле не сравнивается с преимуществами, но, учитывая, что статически типизированное функциональное программирование охватывает большую часть базовых модульных тестов, создается ощущение, что это постоянные издержки, которые можно просто уменьшить без штрафа.

Из этого я могу сделать вывод, что модульные тесты несколько устарели в этом стиле программирования. Конечно, такое утверждение может привести только к религиозным войнам, поэтому позвольте мне свести это к простому вопросу:

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


4
Кстати, если вы не пробовали QuickCheck , вам определенно следует.
Джон Пурди

scalacheck.org - это эквивалент Scala
V-Lamp

Ответы:


8

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

Если у вас есть спецификации, которые можно сопоставить непосредственно с объявлениями функций - хорошо. Но обычно это два совершенно разных уровня абстракций. Модульные тесты предназначены для тестирования отдельных фрагментов кода, написанных в виде белых тестов того же разработчика, который работает над этой функцией. Спецификации обычно выглядят так: «Когда я введу это значение здесь и нажму на эту кнопку, то и это должно произойти». Как правило, такая спецификация приводит к разработке и тестированию более чем одной функции.

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

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

Модульные тесты вводят избыточность, что означает, что при изменении требований код, реализующий их, и тесты, охватывающие этот код, должны быть изменены. Эти накладные расходы, конечно, постоянны, так что можно утверждать, что это не имеет значения. На самом деле, в таких языках, как Ruby, это на самом деле не сравнить с преимуществами, но, учитывая, что статическое типизированное функциональное программирование охватывает многие базовые юнит-тесты, такое ощущение, что это постоянные накладные расходы, которые можно просто уменьшить без штрафа.

В разработке большинство систем безопасности полагаются на избыточность. Например, два перерыва в автомобиле, избыточный парашют для дайвера и т. Д. Та же идея применима к юнит-тестам. Конечно, недостаток кода заключается в большем количестве кода, который можно изменить при изменении требований. Поэтому особенно в модульных тестах важно сохранять их СУХИМЫМИ (следуйте принципу «Не повторяйте себя»). В языке со статической типизацией вам, возможно, придется написать несколько меньших модульных тестов, чем в языке со слабой типизацией. Особенно «формальные» тесты могут не понадобиться - это хорошо, так как это дает вам больше времени для работы над важными юнит-тестами, которые проверяют существенные вещи. И не думайте, что только потому, что вы статические типы, вам не нужны юнит-тесты, все еще есть много места, чтобы вносить ошибки при рефакторинге.


5

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

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

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

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

Часто спецификация (или ее часть) функции может быть выражена как свойства, связывающие возвращаемое значение с аргументами. В этом случае использование QuickCheck (для Haskell) или ScalaCheck (для Scala) может позволить вам записать эти свойства как выражения языка и проверить их на случайность ввода.


1
Немного подробнее о QuickCheck: основная идея заключается в том, что вы выписываете «свойства» (инварианты в вашем коде) и указываете, как генерировать потенциальный ввод. Затем Quickcheck создает массу случайных входных данных и гарантирует, что ваш инвариант выполняется в каждом случае. Это более тщательно, чем модульное тестирование.
Тихон Джелвис

1

Вы можете рассматривать модульные тесты как пример того, как использовать код, вместе с описанием того, почему он полезен.

Вот пример, в котором дядя Боб был достаточно любезен, чтобы присоединиться ко мне в «Игре жизни» Джона Конвея . Я думаю, что это отличное упражнение для такого рода вещей. Большинство тестов являются полностью системными, тестируют всю игру, но первый тест проверяет только одну функцию - ту, которая вычисляет соседей вокруг ячейки. Вы можете видеть, что все тесты написаны в декларативной форме, а поведение, которое мы ищем, четко прописано.

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


0

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

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

Вы можете форсировать prop_encodeстатическим типом.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.