У меня есть общедоступный API C #, который используется многими сторонними разработчиками, написавшими поверх него собственные приложения. Кроме того, API широко используется внутренними разработчиками.
Этот API не был написан с учетом тестируемости: большинство методов класса не являются виртуальными, и вещи не были выделены в интерфейсы. Кроме того, есть несколько вспомогательных статических методов.
По многим причинам я не могу существенно изменить дизайн, не вызывая серьезных изменений для приложений, разработанных программистами, использующими мой API. Однако я все же хотел бы дать внутренним и внешним разработчикам, использующим этот API, возможность писать модульные тесты и иметь возможность имитировать объекты в API.
Есть несколько подходов, которые приходят на ум, но ни один из них не кажется великолепным:
Традиционный подход состоит в том, чтобы заставить разработчиков создавать прокси-класс, которым они управляют, который будет взаимодействовать с моим API. На практике это не сработает, потому что в настоящее время существуют сотни классов, многие из которых являются эффективно строго типизированными объектами передачи данных, которые было бы затруднительно воспроизводить и поддерживать.
Заставить всех разработчиков, использующих API, которые хотят провести модульное тестирование, купить TypeMock . Это кажется резким, заставляя людей платить более 300 долларов за разработчика и потенциально требовать от них изучения другого инструмента для имитации объектов, чем тот, к которому они привыкли.
Пройдите весь проект и сделайте все методы виртуальными. Это позволило бы макетировать объекты с использованием бесплатных инструментов, таких как Moq или Rhino Mocks , но это потенциально могло бы открыть риски безопасности для классов, которые никогда не были получены из. Кроме того, это может привести к серьезным изменениям.
Я мог бы создать инструмент, который бы давал входную сборку, которая выводила бы сборку с теми же пространствами имен, классами и членами, но делала бы все методы виртуальными, а тело метода просто возвращало бы значение по умолчанию для типа возврата. Затем я мог отправлять эту фиктивную тестовую сборку каждый раз, когда выпускал обновление API. Затем разработчики могли бы написать тесты для API против фиктивной сборки, так как у нее были виртуальные члены, которые очень поддаются моделированию. Это может работать, но кажется немного утомительным, чтобы написать собственный инструмент для этого, и я не могу найти существующий, который делает это хорошо (особенно это хорошо работает с дженериками). Кроме того, оно усложняется тем, что разработчикам необходимо использовать две разные сборки, которые могут устареть.
Подобно # 4, я мог бы пройтись по каждому файлу и добавить что-то вроде "#ifdef UNITTEST" в каждый метод и тело, чтобы сделать эквивалент того, что сделал бы инструмент. Для этого не требуется внешний инструмент, но он загрязнит базу кода множеством уродливых "#ifdef".
Есть ли что-то еще, что я не считаю подходящим? Инструмент, подобный тому, что я упомянул в # 4, уже существует?
Опять же, сложным фактором является то, что это довольно большой API (сотни классов и ~ 10 файлов), в котором используются существующие приложения, что затрудняет внесение радикальных изменений в проект.
Там было было несколько вопросов о переполнении стека, которые были общими для модернизации существующего приложения, чтобы сделать его тестируемым, но ни одного Похоже, я решаю проблемы (особенно в контексте широко используемого API со многими сторонними разработчиками). Я также осведомлен о « эффективной работе с устаревшим кодом » и думаю, что в нем есть хороший совет, но я ищу конкретный подход .net с учетом упомянутых выше ограничений.
ОБНОВЛЕНИЕ: Я ценю ответы до сих пор. Вот что поднял Патрик Хагне : «Почему бы не извлечь интерфейсы?» Это действительно работает до некоторой степени, но имеет некоторые проблемы, такие как существующий дизайн имеет много случаев, когда мы выставляем конкретный класс. Например:
public class UserRepository
{
public UserData GetData(string userName)
{
...
}
}
Существующие клиенты, ожидающие конкретного класса (например, «UserData»), сломались бы, если бы им дали «IUserData».
Кроме того, как уже упоминалось в комментариях, есть случаи, когда мы берем класс, а затем выставляем его для удобства. Это может вызвать проблемы, если мы возьмем интерфейс и затем представим его как конкретный класс.
Самая большая проблема для существенного переписывания или редизайна заключается в том, что в текущий API вкладываются огромные средства (тысячи часов разработки и, вероятно, столько же обучения сторонних разработчиков). Поэтому, хотя я согласен с тем, что лучший SOLID слой переписывания или абстракции дизайна (который в конечном итоге может стать новым API), ориентированный на такие элементы, как Принцип разделения интерфейса , был бы плюсом от с точки зрения тестируемости, это было бы большое дело, которое в настоящее время, вероятно, не может быть оправдано с точки зрения затрат.
У нас есть тестирование текущего API, но это более сложное интеграционное тестирование, чем модульное тестирование.
Кроме того, как уже упоминалось Чед Майерс , этот вопрос решает аналогичную проблему, с которой сама платформа .NET сталкивается в некоторых областях.
Я понимаю, что я, вероятно, ищу здесь «серебряную пулю», которой не существует, но вся помощь приветствуется. Важной частью является защита огромных временных вложений многих сторонних разработчиков, а также огромной существующей разработки для создания текущего API.
Все ответы, особенно те, которые рассматривают деловую сторону проблемы, будут тщательно рассмотрены. Спасибо!