Недавно я немного читал о грамотном программировании , и это заставило меня задуматься ... Хорошо написанные тесты, особенно спецификации в стиле BDD, могут лучше объяснить, что делает код, чем проза, и имеют большое преимущество проверка собственной точности.
Я никогда не видел тесты, написанные в линию с кодом, который они тестируют. Это просто потому, что языки не стремятся упростить разделение приложения и тестового кода, когда они написаны в одном и том же исходном файле (и никто не сделал это легко), или есть более принципиальная причина, по которой люди отделяют тестовый код от кода приложения?
testing
unit-testing
bdd
literate-programming
Крис Деверо
источник
источник
Ответы:
Единственное преимущество, которое я могу придумать для встроенных тестов, - это уменьшение количества записываемых файлов. С современными IDE это действительно не так уж важно.
Однако существует ряд очевидных недостатков встроенного тестирования:
источник
Я могу думать о некоторых:
Читаемость. Встраивание «реального» кода и тестов затруднит чтение реального кода.
Код наворочен. Смешение «реального» кода и тестового кода в одни и те же файлы / классы / что угодно может привести к получению больших скомпилированных файлов и т. Д. Это особенно важно для языков с поздним связыванием.
Возможно, вы не хотите, чтобы ваши клиенты / клиенты видели ваш тестовый код. (Мне не нравится эта причина ... но если вы работаете над проектом с закрытым исходным кодом, тестовый код вряд ли поможет клиенту в любом случае.)
Теперь есть возможные обходные пути для каждой из этих проблем. Но ИМО, проще не идти туда в первую очередь.
Стоит заметить, что в первые дни Java-программисты делали подобные вещи; например, включение
main(...)
метода в класс для облегчения тестирования. Эта идея почти полностью исчезла. В отрасли принято внедрять тесты отдельно, используя какой-либо фреймворк.Стоит также отметить, что Literate Programming (как задумал Кнут) никогда не завоевывал популярность в индустрии разработки программного обеспечения.
источник
На самом деле, вы можете думать о дизайне по контракту как об этом. Проблема в том, что большинство языков программирования не позволяют вам писать код, подобный следующему :( Очень легко протестировать предварительные условия вручную, но условия публикации являются реальной проблемой без изменения способа написания кода (огромное отрицательное значение для IMO).
У Майкла Фезерса есть презентация на эту тему, и он упоминает, что это один из многих способов улучшить качество кода.
источник
По многим из тех же причин, по которым вы пытаетесь избежать тесной связи между классами в вашем коде, также рекомендуется избегать ненужной связи между тестами и кодом.
Создание: тесты и код могут быть написаны в разное время разными людьми.
Контроль: если для определения требований используются тесты, вы, безусловно, хотите, чтобы они подчинялись другим правилам о том, кто может их изменить и когда, чем в действительности код.
Возможность повторного использования: если вы поместите тесты в строку, вы не сможете использовать их с другим фрагментом кода.
Представьте, что у вас есть кусок кода, который выполняет свою работу правильно, но оставляет желать лучшего с точки зрения производительности, удобства обслуживания и так далее. Вы решаете заменить этот код новым и улучшенным кодом. Использование того же набора тестов может помочь вам убедиться, что новый код дает те же результаты, что и старый код.
Возможность выбора. Хранение тестов отдельно от кода облегчает выбор тестов, которые вы хотите запустить.
Например, у вас может быть небольшой набор тестов, которые относятся только к коду, над которым вы сейчас работаете, и большой набор, который тестирует весь проект.
источник
Вот некоторые дополнительные причины, которые я могу придумать:
наличие тестов в отдельной библиотеке облегчает связывание только этой библиотеки с вашей средой тестирования, а не с рабочим кодом (этого может избежать какой-то препроцессор, но зачем создавать такую вещь, когда более простое решение - писать тесты в отдельное место)
тесты функции, класса, библиотеки обычно пишутся с точки зрения «пользователей» (пользователя этой функции / класса / библиотеки). Такое «использование кода» обычно пишется в отдельном файле или библиотеке, и тест может быть более понятным или «более реалистичным», если он имитирует эту ситуацию.
источник
Если бы тесты были встроены, было бы необходимо удалить код, необходимый для тестирования, когда вы отправляете продукт своему клиенту. Таким образом, дополнительное место, где вы храните свои тесты, просто разделяет код, который вам нужен, и код, который нужен вашему клиенту .
источник
Эта идея просто сводится к методу «Self_Test» в контексте объектно-ориентированного или объектно-ориентированного проектирования. При использовании скомпилированного объектно-ориентированного языка, такого как Ada, весь код самопроверки будет помечен компилятором как неиспользованный (никогда не вызванный) во время производственной компиляции, и, следовательно, все это будет оптимизировано - ничего из этого не появится в получившийся исполняемый файл.
Использование метода «Self_Test» - очень хорошая идея, и если бы программисты действительно интересовались качеством, они бы все это делали. Однако одна важная проблема заключается в том, что метод «Self_Test» должен иметь строгую дисциплину, поскольку он не может получить доступ к каким-либо деталям реализации и вместо этого должен полагаться только на все другие опубликованные методы в спецификации объекта. Очевидно, что в случае сбоя самопроверки реализация должна будет измениться. Самопроверка должна строго проверять все опубликованные свойства методов объекта, но никогда не полагаться ни на какие детали какой-либо конкретной реализации.
Объектно-ориентированные и объектно-ориентированные языки часто предоставляют именно этот тип дисциплины в отношении методов, внешних по отношению к тестируемому объекту (они обеспечивают реализацию спецификации объекта, предотвращая любой доступ к деталям его реализации и вызывая ошибку компиляции, если обнаружена любая такая попытка). ). Но все внутренние методы объекта имеют полный доступ к каждой детали реализации. Таким образом, метод самотестирования находится в уникальной ситуации: он должен быть внутренним методом по своей природе (очевидно, что самотестирование - это метод тестируемого объекта), но ему необходимо получить всю дисциплину компилятора внешнего метода ( он должен быть независим от деталей реализации объекта). Немногие, если какие-либо языки программирования предоставляют возможность дисциплинировать объект внутренний метод, как если бы это был внешний метод. Так что это важный вопрос дизайна языка программирования.
При отсутствии надлежащей поддержки языка программирования лучший способ сделать это - создать объект-компаньон. Другими словами, для каждого объекта, который вы кодируете (назовем его «Big_Object»), вы также создаете второй объект-компаньон, имя которого состоит из стандартного суффикса, объединенного с именем «реального» объекта (в данном случае, «Big_Object_Self_Test»). "), и чья спецификация состоит из одного метода (" Big_Object_Self_Test.Self_Test (This_Big_Object: Big_Object) return Boolean; "). В этом случае объект-компаньон будет зависеть от спецификации основного объекта, и компилятор будет полностью применять всю дисциплину этой спецификации в отношении реализации объекта-компаньона.
источник
Это происходит в ответ на большое количество комментариев, в которых говорится, что встроенные тесты не выполняются, поскольку трудно или невозможно удалить тестовый код из сборок выпуска. Это неправда. Почти все компиляторы и ассемблеры уже поддерживают это, скомпилированные языки, такие как C, C ++, C #, это делается с помощью так называемых директив компилятора.
В случае c # (я считаю, что и c ++, синтаксис может немного отличаться в зависимости от того, какой компилятор вы используете), это то, как вы можете это сделать.
Поскольку при этом используются директивы компилятора, код не будет существовать в исполняемых файлах, которые создаются, если флаги не установлены. Это также способ заставить программы «один раз написать, дважды скомпилировать» для нескольких платформ / оборудования.
источник
Мы используем встроенные тесты с нашим кодом Perl. Есть модуль Test :: Inline , который генерирует тестовые файлы из встроенного кода.
Я не особенно хорош в организации моих тестов, и нашел, что они легче и с большей вероятностью будут поддерживаться, когда встроены
Отвечая на пару вопросов, поднятых:
+-- 33 lines: #test----
. Когда вы хотите работать с тестом, вы просто расширяете его.Для справки:
источник
Erlang 2 действительно поддерживает встроенные тесты. Любое логическое выражение в коде, которое не используется (например, присвоено переменной или передано), автоматически рассматривается как тест и оценивается компилятором; если выражение ложно, код не компилируется.
источник
Другая причина разделения тестов состоит в том, что вы часто используете дополнительные или даже другие библиотеки для тестирования, чем для реальной реализации. Если вы смешиваете тесты и реализацию, компилятор не может отловить случайное использование библиотек тестов в реализации.
Кроме того, тесты, как правило, содержат больше строк кода, чем тестируемых частей реализации, поэтому вам будет сложно найти реализацию между всеми тестами. :-)
источник
Это не правда Гораздо лучше размещать свои юнит-тесты рядом с производственным кодом, когда производственный код особенно, когда производственная рутина чиста.
Например, если вы разрабатываете под .NET, вы можете поместить свой тестовый код в рабочую сборку, а затем использовать Scalpel, чтобы удалить их перед отправкой.
источник