Подтвердить что ты не робот

С++ Unit Testing Legacy Code: Как обращаться С#include?

Я только что начал писать модульные тесты для устаревшего модуля кода с большими физическими зависимостями, используя директиву #include. Я имел дело с ними несколькими способами, которые казались слишком утомительными (предоставляя пустые заголовки, чтобы сломать длинные списки зависимостей #include и использовать #define для предотвращения компиляции классов) и искали некоторые более эффективные стратегии для решения этих проблем.

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

Кто-нибудь знает некоторые лучшие практики?

4b9b3361

Ответ 1

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

Переходите на страницу 127: Случай ужасных включений зависимостей. (Теперь я даже не в милях от Майкла Перо, но здесь как-короткий-как-я мог бы управлять ответом.. )

Проблема. На С++, если классA должен знать о ClassB, объявление класса B прямо или частично включено в исходный файл ClassA. И поскольку мы, программисты, любим воспринимать это не в последнюю очередь, файл может рекурсивно включать в себя миллион других транзитно. Строит взять годы.. но эй по крайней мере он строит.. мы можем подождать.

Теперь сказать, что "экземпляр ClassA под тестовым жгутом затруднен" - это преуменьшение. (Цитируя пример MF - Планировщик - наш ребенок с проблемами с плакатами с депрессиями в изобилии.)

#include "TestHarness.h"
#include "Scheduler.h"
TEST(create, Scheduler)     // your fave C++ test framework macro
{
  Scheduler scheduler("fred");
}

Это приведет к тому, что включает дракона с ошибкой ошибок сборки.
Удар № 1 Терпение-н-Персистирование. Возьмите каждый из них по одному за раз и решите, действительно ли нам нужна эта зависимость. Пусть предположим, что SchedulerDisplay является одним из них, метод displayEntry которого вызывается в Scheduler ctor.
Blow # 2 Fake-it-till-you-make-it (спасибо RonJ):

#include "TestHarness.h"
#include "Scheduler.h"
void SchedulerDisplay::displayEntry(const string& entryDescription) {}
TEST(create, Scheduler)
{
  Scheduler scheduler("fred");
}

И появляется популярность, и все ее транзитивные включают. Вы также можете повторно использовать методы Fake, инкапсулируя его в файл Fakes.h, который будет включен в ваши тестовые файлы.
Удар № 3 Практика. Возможно, это не всегда так просто.. но вы получаете эту идею. После первых нескольких дуэлей процесс разлома депиляции станет легким-n-механическим.

Предостережения (Я упоминал, что есть оговорки?:)

  • Нам нужна отдельная сборка для тестовых примеров в этом файле; мы можем иметь только одно определение для метода SchedulerDisplay:: displayEntry в программе. Поэтому создайте отдельную программу для тестов планировщика.
  • Мы не нарушаем никаких зависимостей в программе, поэтому мы не делаем код более чистым.
  • Вам нужно поддерживать эти подделки до тех пор, пока нам нужны тесты.
  • Ваше чувство эстетики может быть оскорблено какое-то время.. просто кусайте свою губу и "несите с нами на лучшее завтра".

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

Для более.. пожалуйста, прочитайте книгу. Бесценный. Борьба с братом!

Ответ 2

Поскольку вы тестируете устаревший код, я предполагаю, что вы не можете реорганизовать упомянутый код на меньшее количество зависимостей (например, используя pimpl idiom)

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

Ответ 3

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

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

Я попытался найти способ добавить тесты устройств в приложение, но в итоге просто застрял в catch-22:

  • Чтобы написать полноценные модульные тесты, код нужно будет реорганизовать.
  • Без модульных тестов это будет слишком опасно для рефакторинга кода.

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

Иногда лучше всего работать над кодом так, чтобы он был "разработан" для работы.

Ответ 4

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

Это полностью устранит вашу проблему #include. Все, что вам нужно будет сделать, - это повторно реализовать интерфейсы в включенных файлах, чтобы делать все, что захотите, а затем просто привязать к файлам макетных объектов, которые вы создали, для реализации интерфейсов во включенном файле.

Большим недостатком этого метода является более сложная система сборки.

Ответ 5

Если вы продолжаете писать коды-заглушки/макеты/поддельные коды, вы рискуете провести модульное тестирование в классе, который имеет другое поведение, а затем компилируется в основном проекте.

Но если они включены и не имеют добавленного поведения, тогда это ОК.

Я бы попробовал ничего не менять при включении при выполнении модульного тестирования, чтобы вы были уверены (насколько вы можете быть в устаревшем коде:)), что вы проверяете реальный код.

Ответ 6

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

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

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