Абстрактные классы сложно тестировать. Рассмотрим ситуации применения абстрактных классов и последующего рефакторинга для проведения юнит тестирования.
Выделить реальный интерфейс
Существует специализированный абстрактный класс, но все клиенты используют его конкретные реализации через единый публичный интерфейс:
Классы-потомки реализуют интерфейс, определенный абстрактными методами класса. Для повышения тестируемости этот интерфейс выделяется. Абстрактный класс превращается в конкретный и в его конструктор передается объект классов-потомков, реализующих выделенный интерфейс.
Применяется шаблон проектирования стратегия.
Бывший абстрактный класс теперь тестируется используя мок-объект нового интерфейса. Все просто.
Выделить хелпер
Абстрактный класс используется для исключения повторений в наследуемых классах. Классы-потомки используются напрямую.
Абстрактный класс работает как хелпер (helper).
Чтобы повысить тестируемость, не повторяющийся код переносится в существующие классы. Оставшиеся методы выделяются в хелпер и передаются через интерфейс в конструкторы конкретных классов.
Базовый класс удаляется. Этот способ снова приводит дизайн к конкретным классам, которые просто и легко тестировать.
Совет
Предпочитайте сложную сеть простых объектов над простой сетью сложных. Ключ к трестируемому коду — маленькие строительные блоки и слабая связанность.
Комбинация ситуаций
Встречаются ситуации когда базовый класс содержит как публичный интерфейс, так и защищенные методы-помощники. В этом случае вспомогательные методы отправляются в один класс (ситуация 2), а для наследников реализуется шаблон стратегия (ситуация 1).
Когда в абстрактном классе часть методов виртуальные, а часть реализованные, еще возможно провести рефакторинг. Например, превратить классы наследники в стратегию. Но, такой случай — хороший индикатор, что ответственности требуют пересмотра.
Пишите в комментариях примеры когда сложно обойтись без абстрактного класса.