Перечисления - это просто конечные типы с собственными (надеюсь значащими) именами. Перечисление может иметь только одно значение, например, voidкоторое содержит только null(некоторые языки называют это unitи используют имя voidдля перечисления без элементов!). Может иметь два значения, например, boolкоторое имеет falseи true. Может быть три, как colourChannelс red, greenи blue. И так далее.
Если два перечисления имеют одинаковое количество значений, то они «изоморфны»; то есть, если мы систематически переключаем все имена, то мы можем использовать одно вместо другого, и наша программа не будет вести себя по-другому. В частности, наши тесты не будут вести себя иначе!
Например, resultсодержание win/ lose/ drawизоморфно вышеприведенному colourChannel, поскольку мы можем заменить, например, colourChannelна result, redс win, greenс loseи blueс drawи до тех пор, пока мы делаем это везде (производители и потребители, анализаторы и сериализаторы, записи базы данных, файлы журналов и т. Д. ) тогда не будет никаких изменений в нашей программе. Любые « colourChannelтесты», которые мы написали, все равно пройдут, хотя их больше нет colourChannel!
Кроме того, если перечисление содержит более одного значения, мы всегда можем переставить эти значения, чтобы получить новое перечисление с тем же числом значений. Поскольку число значений не изменилось, новое расположение изоморфно старому, и, следовательно, мы могли бы отключить все имена, и наши тесты все равно бы прошли (обратите внимание, что мы не можем просто переключить определение; мы должны все еще отключите все сайты использования также).
Это означает, что для машины перечисления являются «различимыми именами» и ничем иным . Единственное, что мы можем сделать с перечислением, это определить, являются ли два значения одинаковыми (например, red/ red) или разными (например, red/ blue). Так что это единственное, что может сделать «модульный тест», например
( red == red ) || throw TestFailure;
(green == green) || throw TestFailure;
( blue == blue ) || throw TestFailure;
( red != green) || throw TestFailure;
( red != blue ) || throw TestFailure;
...
Как говорит @ jesm00, такой тест проверяет реализацию языка, а не вашу программу. Эти тесты никогда не являются хорошей идеей: даже если вы не доверяете языковой реализации, вы должны тестировать ее извне , поскольку нельзя доверять правильному запуску тестов!
Такова теория; как насчет практики? Основная проблема, связанная с такой характеристикой перечислений, заключается в том, что программы «реального мира» редко бывают автономными: у нас есть устаревшие версии, удаленные / встроенные развертывания, исторические данные, резервные копии, действующие базы данных и т. Д., Поэтому мы никогда не сможем «отключиться» все вхождения имени, не пропуская некоторые использования.
Однако такие вещи не являются «обязанностью» самого перечисления: изменение перечисления может нарушить связь с удаленной системой, но, наоборот, мы могли бы решить такую проблему, изменив перечисление!
В таких случаях, перечисление является красно-селедка: что если одна система нуждается в этом , чтобы быть этот путь, и еще нужно, чтобы это было что путь? Это не может быть и то и другое, независимо от того, сколько тестов мы пишем! Настоящим виновником здесь является интерфейс ввода / вывода, который должен производить / потреблять четко определенные форматы, а не «любое целое число, которое выбирает интерпретатор». Таким образом, реальным решением является тестирование интерфейсов ввода / вывода : с помощью модульных тестов, чтобы проверить, анализирует ли он / печатает ли ожидаемый формат, и с помощью интеграционных тестов, чтобы проверить, что формат действительно принят другой стороной.
Мы все еще можем задаться вопросом, достаточно ли тщательно прорабатывается перечисление, но в этом случае перечисление снова представляет собой красную сельдь. На самом деле нас беспокоит сам набор тестов . Мы можем обрести уверенность здесь несколькими способами:
- Покрытие кода может сказать нам, достаточно ли разнообразных значений enum, поступающих из набора тестов, для запуска различных ветвей в коде. Если нет, мы можем добавить тесты, которые запускают непокрытые ветви, или генерировать более широкий спектр перечислений в существующих тестах.
- Проверка свойств может сказать нам, достаточно ли разнообразия ветвей в коде для обработки возможностей времени выполнения. Например, если код только обрабатывает
red, а мы только тестируем red, то мы имеем 100% охват. Свойство шашка будет (пытаться) генерировать контрпримеры наших утверждений, например, генерируя greenи blueценность , которые мы забыли проверить.
- Мутационное тестирование может сказать нам, действительно ли наши утверждения проверяют перечисление, а не просто следуют ветвям и игнорируют их различия.