Все юнит-тесты в одном исполняемом файле или их разбили?

12

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

Причина, по которой я спрашиваю, заключается в том, что я сейчас использую CUnit для тестирования библиотеки, над которой я работаю. Тесты разбиты на отдельные наборы, которые скомпилированы в один исполняемый файл с выводом на печать для сбоев. Теперь системой сборки для этой библиотеки является CMake (которая, несмотря на свое название, не имеет ничего общего с CUnit), которая поставляется с собственной платформой тестирования, CTest . CTest позволяет мне зарегистрировать список исполняемых файлов, которые служат тестами.

Я размышляю, стоит ли использовать CTest для автоматизированного тестирования. Однако это потребовало бы от меня разбивки написанных мною тестов на отдельные цели компиляции. В противном случае я не смогу использовать некоторые расширенные функции CTests, такие как выборочное выполнение тестов.

Я понимаю, что это больше вопрос о том, какие инструменты использовать, их обработку и соглашения, но кроме этого, есть ли другие причины предпочитать один исполняемый файл теста отдельным? Или наоборот?

Бенджамин Клостер
источник
Разделите их на отдельные исполняемые файлы по классам. Каждый класс должен иметь свой собственный модульный тест, если класс не является модульным для тестирования и, следовательно, не требует косвенного тестирования другими классами.
Брайан
1
Если у вас есть большая библиотека с сотнями классов, каждый с модульным тестом, время сборки будет намного больше, если вы создадите полный двоичный файл для каждого, по сравнению с одним (или несколькими) большими двоичными файлами. Кроме того, есть много make-файлов для управления, каждый из которых имеет отдельные линии ссылок.
JBRWilkinson
Это не так уж и много. Менее 20 модулей. Я также могу скомпилировать их все с одинаковыми флагами, поэтому CMake может генерировать Makefile без особой работы с моей стороны.
Бенджамин Клостер

Ответы:

5

Мне нравится, когда мои автоматические тесты выполняются в отдельных двоичных файлах или, по крайней мере, сгруппированы для каждой группы «принадлежат друг другу», а затем вызывают их из простого сценария оболочки (где ненулевой код выхода сигнализирует об ошибке, и вывод на stderr может быть захвачен записать объяснение). Таким образом, я сохраняю полную гибкость в тестировании - я могу запускать отдельные тесты непосредственно из командной строки, я могу создавать всевозможные необычные сценарии, если хочу, могу переупорядочивать их по своему усмотрению, без перекомпиляции и т. Д.

Но что более важно, это также позволяет мне включать тесты, написанные на разных языках или использующие разные наборы инструментов в одном прогоне. Например, написанные мной модульные тесты, скорее всего, написаны на основном языке проекта, и их запуск заключается в создании и вызове двоичных файлов; но я также хочу протестировать свою базу данных, и для этого я мог бы подать сценарии SQL непосредственно в базу данных; Я мог бы захотеть запустить какой-то инструмент статического анализа кода в моем коде (даже если это просто какой-то линтер). Я могу захотеть запустить мой статический HTML через проверку достоверности. Я мог бы выполнить grepкоманду над базой кода, чтобы проверить на наличие подозрительных конструкций, нарушений стиля кодирования или ключевых слов «красный флаг». Возможности бесконечны - если его можно запустить из командной строки и придерживаться «нулевого состояния выхода означает ОК», я могу его использовать.

tdammers
источник
Аргументация, не зависящая от языка, является очень хорошим аргументом, поскольку я планирую реализовать привязки Python для библиотеки в будущем. Благодарность!
Бенджамин Клостер
@tdammers: какие-то конкретные рамки тестирования?
JBRWilkinson
@JBRWilkinson: всего лишь 30-строчный сценарий оболочки. Для большинства языков, которые я использую, у меня мало библиотек для общих задач тестирования, таких как запуск функции, сравнение результата с ожидаемым значением и выдача, когда они не совпадают. Но любая данная инфраструктура модульного тестирования может быть легко интегрирована в такую ​​«метасистему», если она может запускаться из командной строки и сигнализировать об успехе / неудаче через свое состояние выхода.
tdammers
2

Я склонен иметь одну библиотеку для модульных тестов одного приложения (или для пакета библиотек, который обычно используется совместно). В этой библиотеке я пытаюсь реплицировать или аппроксимировать пространства имен тестируемых объектов для тестовых приборов (в основном я использую NUnit). Это упрощает компиляцию, поскольку в .NET при создании каждого двоичного файла присутствуют накладные расходы, которые увеличивают время сборки решения из 20 проектов по сравнению с решением из 10 проектов с тем же LOC. Тестовые двоичные файлы в любом случае не распространяются, поэтому любая организация тестов в двоичные файлы предназначена для вашего удобства, и я обычно нахожу, что YAGNI применяется здесь как и везде.

Теперь у меня обычно нет тех соображений, которые есть у tdammers; мой код практически полностью написан на одном языке, и любой тест, включающий строки SQL, не является модульным тестом (если только вы не проверяете, что производитель запросов возвращает ожидаемую строку SQL с учетом определенных критериев), и я практически никогда не тестирую модульно Пользовательский интерфейс (во многих ситуациях это просто невозможно). Я также использую библиотеку модульного тестирования, которая хорошо принята сторонними инструментами, такими как build-боты и плагины IDE, поэтому любые проблемы с запуском отдельных тестов, частичных пакетов и т. Д. Минимальны.

Keiths
источник
1
Я полагаю, что это вопрос культуры. В среде .NET я, вероятно, рассуждаю так же, как и вы.
tdammers