Как программист, я искренне увлекся философией TDD и прилагаю усилия к проведению обширных модульных тестов для любого нетривиального кода, который я пишу. Иногда этот путь может быть болезненным (поведенческие изменения, вызывающие каскадные изменения нескольких модульных тестов; большое количество необходимых лесов), но в целом я отказываюсь программировать без тестов, которые я могу запустить после каждого изменения, и мой код гораздо менее глючит как результат.
В последнее время я играю с Haskell и его постоянной библиотекой тестирования QuickCheck. В отличие от TDD, QuickCheck делает акцент на тестировании инвариантов кода, то есть определенных свойств, которые сохраняются во всех (или основных подмножествах) входных данных. Быстрый пример: алгоритм стабильной сортировки должен давать тот же ответ, если мы запускаем его дважды, должен иметь увеличивающийся вывод, должен быть перестановкой ввода и т. Д. Затем QuickCheck генерирует различные случайные данные для проверки этих инвариантов.
Мне кажется, по крайней мере для чистых функций (то есть для функций без побочных эффектов - и если вы правильно выполняете насмешку, вы можете преобразовать грязные функции в чистые), то инвариантное тестирование может заменить модульное тестирование строгим надмножеством. из этих возможностей. Каждый модульный тест состоит из ввода и вывода (в императивных языках программирования «выход» - это не только возвращение функции, но и любое измененное состояние, но это может быть заключено в капсулу). Можно было бы создать генератор случайных входов, который был бы достаточно хорош, чтобы покрыть все входы модульного теста, которые вы бы создали вручную (а затем и некоторые, потому что он генерировал бы случаи, о которых вы даже не думали); если вы обнаружите ошибку в вашей программе из-за какого-либо граничного условия, вы улучшите свой генератор случайных входов, чтобы он тоже генерировал этот случай.
Таким образом, проблема заключается в том, возможно ли сформулировать полезные инварианты для каждой проблемы. Я бы сказал, что так: намного проще, когда у вас есть ответ, чтобы увидеть, является ли он правильным, чем вычислять ответ в первую очередь. Размышление об инвариантах также помогает прояснить спецификацию сложного алгоритма гораздо лучше, чем специальные тестовые случаи, которые поощряют своеобразное индивидуальное осмысление проблемы. Вы можете использовать предыдущую версию вашей программы в качестве реализации модели или версию программы на другом языке. И т. Д. В конце концов, вы могли бы охватить все ваши бывшие тесты без необходимости явного кодирования ввода или вывода.
Я сошел с ума, или я к чему-то?