2014-12-30 4 views
3

Я пытаюсь проверить код устаревшего кода на C++. В частности, у меня есть иерархия классов, скажем, A < B < C (то есть A является подклассом B, а B является подклассом C), и существует глобальная ссылка на объект типа C, который используется со всех сторон код системы (singleton pattern). Цель состоит в том, чтобы заменить объект C на некоторый поддельный объект (фактически, для доступа к базе данных используется C).Как подделывать классы C++, содержащие не виртуальные функции?

Моя первая попытка была ввести интерфейсы IA, IB, and IC (которые содержат чистые виртуальные версии функций соответствующего класса), пусть каждый класс реализовать свой интерфейс, и изменить тип глобальной C ссылкой на IC. В настройке моих тестов я бы затем заменил объект C, связанный глобально с моей собственной реализацией IC, что сделало всю систему используемой моей поддельной реализацией.

Однако классы A, B и C содержат в себе немало не виртуальных функций. Теперь, если я сделаю классы наследуемыми от своих интерфейсов, я бы изменил семантику этих функций от не виртуального к виртуальному (Перья обсуждают эту проблему в «Эффективной работе с устаревшим кодом», с.367). Другими словами: я должен проверять каждый вызов на мой глобальный объект, и я должен убедиться, что после моих изменений все же будут вызываться одни и те же функции. Это звучит, как много работы ERROR PRONE для меня.

Я также думал о том, чтобы не-виртуальных функциях «окончательные», то есть сказать компилятору, что функции A, B и C не должны быть скрыты в подклассах (который сделал бы составитель сказать мне все потенциально опасные функции B и C - если функция не скрыта в базовом классе, вышеупомянутый эффект вообще не может произойти), но это, похоже, не поддерживается C++ (мы еще не используем C++ 11, но даже его окончательный ключевое слово только кажется применимым к виртуальным функциям).

Чтобы сделать ситуацию еще более сложной, классы A, B и C также содержат общедоступные атрибуты, виртуальные функции, а также некоторые функции шаблона.

Так что мой вопрос: как справиться с ситуацией, описанной выше? Есть ли возможности C++, которые я пропустил, и которые могут помочь в моем сценарии? Любые шаблоны проектирования? Или даже инструменты рефакторинга? Мое основное требование состоит в том, что изменения должны быть максимально безопасными, поскольку классы, которые я хотел бы подделать, имеют очень важное значение для системы ... Я также был бы доволен «уродливым» решением, которое позволило бы мне поставить тесты (и которые могут быть реорганизованы позже, если система надлежащим образом покрыта испытаниями).

Редактировать: Я испортил свою иерархию наследования (она перевернулась) - это исправлено сейчас.

Редакция 2: Мы, наконец, закончили следующим образом: мы сделали только виртуальные функции, которые нам действительно нужны для наших текущих тестовых случаев. Затем мы проверили каждый вызов на эти методы (который был управляемым). Это позволило нам насмехаться над нашим классом с помощью Google Mocks. Имея все больше и больше тестовых примеров, мы надеемся, что наши изменения будут сохраняться со временем. Обратите внимание, что, задавая свой вопрос, я думал, что Google Mocks может только издеваться над чистыми интерфейсами; это не корпус, что позволяет использовать инкрементный подход, как описано выше.

+0

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

+0

Являются ли классы 'A' и/или' B' конкретными? Если да, то они должны * быть? –

+0

@Puppy: наша система имеет довольно много глобальных объектов, и я рассматриваю это как типичную проблему с курицей и яйцом: Чтобы безопасно избавиться от глобалов, мне нужны тесты, а для написания тестов мне нужно избавиться глобалы. Таким образом, я сейчас пытаюсь получить тесты на месте с минимальными и безопасными, как и всевозможными рефакторингами, и я планирую позже решить проблему с полным глобалом. – csoltenborn

ответ

1

EDIT: В комментариях к Dietmar Kühl ниже этот подход не сработает, чтобы подделать шаблоны функций-членов.

Я не уверен, что это сработает, но попробуйте сделать поддельный класс (не виртуальный) в отдельном файле .cpp. Затем свяжите тесты с поддельным классом, но не реальным. Теоретически у двух shojld есть тот же ABI (Application Binary Interface), поэтому они должны быть совместимы.

+0

Если бы не те шаблоны функций-членов, которые находятся в этом классе, это может сработать. С шаблонами функций-членов почти наверняка потребуется, по крайней мере, скомпилировать тестовую версию. –

+0

А, я пропустил это. Да, с функциями шаблона члена этот подход не будет работать для подделки этих функций. – Maz

+0

Он может работать, если вы замените заголовки, а также библиотеки. Вы можете определить макрос, а затем сделать включение включенным для этого макроса. – Puppy

2

Я собираюсь предложить изменить C в шаблон, где тип шаблона является фактическим кодом доступа к базе данных. Затем в вашей живой программе вы используете C<LiveDatabase> везде, где вы сейчас используете C, и при тестировании вы используете C<MockDatabase>. Затем A и B остаются неизменными, и все части внутренней работы C остаются неизменными, при этом разные вызовы базы данных отличаются (в их вызовах делегированной реализации).

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

+0

Есть ли причина предпочесть шаблон для простого извлечения слоя базы данных в отдельный объект, предоставленный при создании C? –

+0

@Dave Bacher шаблон может обеспечить лучшую производительность, позволяя компилятору знать типы во время компиляции. –

+0

«Идея шаблона» - интересное предложение, которое я обязательно буду оценивать в следующем году - спасибо за предложение! – csoltenborn