Я работаю над тем, чтобы сделать мои классы модульно-тестируемыми, используя внедрение зависимостей. Но некоторые из этих классов имеют много клиентов, и я не готов реорганизовать их все, чтобы начать передавать зависимости. Поэтому я пытаюсь сделать это постепенно; пока сохраняем зависимости по умолчанию, но позволяем переопределять их для тестирования.
Один из подходов, который я придумаю, - это просто перевести все «новые» вызовы в их собственные методы, например:
public MyObject createMyObject(args) {
return new MyObject(args);
}
Затем в своих модульных тестах я могу просто создать подкласс этого класса и переопределить функции create, чтобы они вместо этого создавали поддельные объекты.
Это хороший подход? Есть ли недостатки?
В целом, нормально ли иметь жестко запрограммированные зависимости, если вы можете заменить их для тестирования? Я знаю, что предпочтительный подход состоит в том, чтобы явно требовать их в конструкторе, и я бы хотел в конечном итоге туда добраться. Но мне интересно, если это хороший первый шаг.
Один недостаток, который только что возник у меня: если у вас есть реальные подклассы, которые вы хотите протестировать, вы не можете повторно использовать тестовый подкласс, который вы написали для родительского класса. Вам нужно будет создать тестовый подкласс для каждого реального подкласса, и он должен будет переопределить те же функции создания.
Ответы:
Это хороший подход для начала. Обратите внимание, что наиболее важно покрыть существующий код модульными тестами; Как только вы пройдете тесты, вы сможете более свободно выполнять рефакторинг, чтобы улучшить свой дизайн.
Таким образом, первоначальный смысл заключается не в том, чтобы сделать дизайн элегантным и блестящим, а просто в том, чтобы сделать блок кода тестируемым с наименьшими рисками изменений . Без модульных тестов вы должны быть очень консервативны и осторожны, чтобы не нарушить ваш код. Эти первоначальные изменения могут даже сделать код более неуклюжим или уродливым в некоторых случаях - но если они позволят вам написать первые модульные тесты, в конечном итоге вы сможете рефакторироваться в соответствии с идеальным дизайном, который вы имели в виду.
Фундаментальная работа для чтения на эту тему - Эффективная работа с устаревшим кодом . Он описывает трюк, который вы показываете выше, и многие, многие другие, используя несколько языков.
источник
Guice-люди рекомендуют использовать
для всех свойств вместо простого добавления @Inject к их определению. Это позволит вам рассматривать их как обычные bean-компоненты, которые вы можете использовать
new
при тестировании (и настройке свойств вручную) и @Inject в производственной среде.источник
Когда я сталкиваюсь с этим типом проблемы, я идентифицирую каждую зависимость моего класса для тестирования и создаю защищенный конструктор, который позволяет мне передавать их. Это фактически то же самое, что создание сеттера для каждого, но я предпочитаю объявление зависимостей в моих конструкторах, чтобы я не забывал создавать их экземпляры в будущем.
Итак, мой новый класс для тестирования модулей будет иметь 2 конструктора:
источник
Возможно, вы захотите рассмотреть идиому «геттер создает», пока не сможете использовать внедренную зависимость.
Например,
Это позволит вам выполнить внутренний рефакторинг, чтобы вы могли ссылаться на свой myObject через геттер. Вам никогда не придется звонить
new
. В будущем, когда все клиенты будут настроены для разрешения внедрения зависимостей, все, что вам нужно сделать, это удалить код создания в одном месте - получателе. Сеттер позволит вам вводить фиктивные объекты по мере необходимости.Приведенный выше код является только примером, и он непосредственно представляет внутреннее состояние. Вы должны тщательно продумать, подходит ли этот подход для вашей кодовой базы.
Я также рекомендовал бы вам прочитать « Эффективная работа с устаревшим кодом », в котором содержится множество полезных советов, чтобы добиться успеха в крупном рефакторинге.
источник