Как вы вводите модульное тестирование в большой, унаследованной (C / C ++) кодовой базе? - PullRequest
72 голосов
/ 14 апреля 2009

У нас есть большое мультиплатформенное приложение, написанное на языке C. (с небольшим, но растущим количеством C ++). В течение многих лет оно развивалось со многими функциями, которые можно ожидать в больших приложениях на C / C ++:

  • #ifdef ад
  • Большие файлы, затрудняющие выделение тестируемого кода
  • Функции, которые слишком сложны, чтобы их можно было легко проверить

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

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

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

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

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

Лично я бы предпочел, чтобы разработчики подумали об их внешних зависимостях и разумно написали свои собственные заглушки. Но это может быть ошеломляющим, чтобы заглушить все зависимости для ужасно заросшего файла из 10000 строк. Может быть трудно убедить разработчиков, что им нужно поддерживать заглушки для всех своих внешних зависимостей, но так ли это правильно? (Еще один аргумент, который я слышал, заключается в том, что сопровождающий подсистемы должен поддерживать заглушки для своей подсистемы. Но мне интересно, приведет ли "принуждение" разработчиков к написанию своих заглушек к лучшему модульному тестированию?)

#ifdefs, конечно, добавляет еще одно целое измерение к проблеме.

Мы рассмотрели несколько основ модульных тестов на основе C / C ++, и есть много вариантов, которые выглядят хорошо. Но мы не нашли ничего, что могло бы облегчить переход от «шарика кода без юнит-тестов» к «юнит-тестируемому коду».

Итак, вот мои вопросы ко всем, кто прошел через это:

  • Что является хорошей отправной точкой? Мы идем в правильном направлении, или мы упускаем что-то очевидное?
  • Какие инструменты могут быть полезны, чтобы помочь с переходом? (желательно бесплатный / открытый исходный код, поскольку наш бюджет сейчас примерно равен «нулю»)

Обратите внимание, что наша среда сборки основана на Linux / UNIX, поэтому мы не можем использовать инструменты только для Windows.

Ответы [ 12 ]

1 голос
/ 23 января 2013

Во всем этом есть философский аспект.

Вы действительно хотите протестированный, полностью функциональный, аккуратный код? Это твоя цель? Получаете ли вы какую-либо выгоду от этого?

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

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

Но, если вы чувствуете, что сейчас тратите дополнительные часы на поддержание работы неопрятной системы, вы действительно думаете, что это изменится, когда код станет аккуратным и красивым? Нет ... как только код станет красивым и аккуратным, люди получат все это свободное время, чтобы полностью уничтожить его снова в первый доступный срок.

В конце концов, управление создает приятное рабочее место, а не код.

1 голос
/ 16 апреля 2009

Упростите использование тестов.

Я бы начал с установки "автоматически запускается" на место. Если вы хотите, чтобы разработчики (включая вас самих) писали тесты, упростите их запуск и посмотрите результаты.

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

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

Проверка того, что можно проверить

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

Я весьма удивлен тем, что обычно находят простые тесты. Сбор низко висящих фруктов ни в коем случае не бесполезен.

Глядя на это по-другому: вы бы не планировали поддерживать этот гигантский беспорядок в волосяных шариках, если бы он не был успешным продуктом. Ваш текущий контроль качества не является полным отказом, который необходимо заменить. Вместо этого используйте модульные тесты там, где их легко выполнить.

(Вы должны сделать это, однако. Не попадайтесь в ловушку "исправления всего" вокруг процесса сборки.)

Научите, как улучшить базу кода

Любая база кода с этой историей требует улучшений, это точно. Вы никогда не будете рефакторинг всего этого, все же.

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

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

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


Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...