понедельник, 17 октября 2011 г.

TDD в условиях плотного использования ATL/COM

Попытки внедрить TDD для проектов, которые активно используют ATL/COM, очень часто заканчивались для меня неудачно из-за сложности задачи, которая приводит к тому, что просто опускались руки из-за того что не знал как подступится к задаче и гугл с яндексом в те моменты молчали и не желали делится мудростью других людей.

И проблема была даже не в том "как протестировать сам COM-компонент?" - это не представляло особой проблемы, потому что был и есть явно заданный интерфейс класса, единственной особенностью которого является то, как создаётся объект этого класса. Дальше всё сводилось к классическому подходу для TDD.
Основной проблемой являлся вопрос "как протестировать класс, который уже активно использует COM-компоненты?"

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

И корень проблемы был даже не в том, что приходилось создавать эти классы-заглушки - это то как раз было довольно просто и входило для меня в область допустимых накладных расходов от применения TDD. Проблема была в том как обеспечить создание и использование классов-заглушек не изменяя кучи кода.

Подсказкой к решению является скрытая причина этой ситуации - у нас уже по всему коду протянута неявная зависимость от реестра системы. Соответственно для удобства в написании тестов я должен обеспечить единую точку доступа и контроля этой зависимости. Что привело меня к следующей идее: введём дополнительный архитектурный слой с единичным контейнером, который контролирует процесс создания COM-компонентов (по аналогии с слоем работы с объектами базы данных).

Единый контейнер прийдёт к нам со всеми своими достоинствами и недостатками:
  • единая точка контроля создания компонентов
  • у нас должны быть гарантия что контейнер объектов существует в едином экземпляре, иначе у нас будет возможна ситуация, когда в одной и той же функции у нас будет мок-COM-объект и нормальный COM-объект - это приводит нас к синглтону - т.е. у нас будет или глобальная точка входа или обязательный параметр для функций, которые создают объекты
  • дополнительный слой абстракции

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

P.S.
Я таки дописал хоть один пост за прошедший год.