Инъекция зависимостей необходима для модульного тестирования?

56

Является ли использование внедрения зависимостей (DI) необходимым для модульного тестирования?

Я не могу придумать другой альтернативы для изоляции кода, чтобы его можно было проверить. Кроме того, все примеры, которые я когда-либо видел, используют этот шаблон. Это потому, что это единственный жизнеспособный вариант или есть другие альтернативы?

Том Сквайрс
источник
5
Внедрение зависимостей не является обязательным, но более широкая концепция инверсии контроля.
Джереми Хейлер
Здесь есть что сказать по шкале. Если у меня небольшая кодовая база с очень небольшим количеством слоев, DI может оказаться бесполезным.
JB King
@JBKing, если у вас небольшая база кода, вам не нужны слои или модульное тестирование
Sklivvz
1
Чтобы сделать ваш код тестируемым, нужно много дизайнерских решений. Начните писать тесты и узнайте.
Натан Купер

Ответы:

43

DI делает модульное тестирование намного проще. Но вы все равно можете писать модульные тесты без DI. Многие модульные тесты были написаны еще до того, как DI стал широко распространенным. (Конечно, некоторые из этих используемых методов идентичны или очень похожи на DI, не зная, что у него есть причудливое название :-)

Я сам много использовал, например, интерфейсы и фабрики, прежде чем узнал о DI. Фактическое имя класса фабрики может быть прочитано из файла конфигурации или передано в SUT в качестве аргумента.

Другой подход - использование синглетонов (или глобально доступных данных в целом). Да, я знаю, что это не рекомендуется многими (включая меня) вообще. Тем не менее он может быть жизнеспособным в определенных ситуациях, особенно если синглтон содержит статические данные конфигурации, которые не являются специфичными для тестового примера, но различаются между производственной и тестовой средой. Конечно, у него есть известные проблемы, поэтому DI лучше, если вы можете его использовать. Но часто (например, в устаревших системах) вы не можете.

Говоря об этом, Эффективная работа с устаревшим кодом описывает множество хитростей, позволяющих тестировать устаревший код. Многие из них не хороши, и не предназначены для долгосрочного решения. Но они позволяют вам создавать первые ценные модульные тесты для другой непроверяемой системы ... которая позволяет вам начать рефакторинг и, в конечном итоге (среди прочих), ввести DI.

Петер Тёрёк
источник
Согласен, DI особенно полезен для насмешек. Но есть много сценариев, когда DI бесполезен в тестах.
Кемода
@Кемода, например, что?
BЈовић
@VJovic Например, автономные объекты. Я думаю о методе, который принимает некоторые аргументы и выполняет некоторые вещи, но не зависит от другого компонента (таким образом, нет необходимости в DI).
Кемода
@Kemoda Похоже, вы описываете функциональное программирование и используете DI. Вы вводите свои зависимости как параметры метода.
Эрик Дитрих
3
@ Huggie, почему детали реализации здесь просочились? Внедренная зависимость обычно скрывается за интерфейсом, и весь смысл в том, что клиентский класс не имеет представления о том, является ли фактическая реализация этой зависимости действительным производственным классом или имитацией, и не заботится о нем. Его создание происходит за пределами клиентского класса, он видит только готовый экземпляр.
Петер Тёрёк
56

Разделение важно для модульного тестирования. DI - отличный способ добиться развязки.

nlawalker
источник
17
Очень верное утверждение, которое никоим образом не отвечает на вопрос.
Трэвис
10

В зависимости от технологий, которые вы используете, вы можете изолировать зависимости без использования DI. Например, в мире .NET Moles позволяет изолировать зависимости без шаблона DI.

Тем не менее, я считаю, что эти инфраструктуры изоляции написаны и предназначены для ситуаций в вашем коде с внешними зависимостями (файловая система, база данных и т. Д.). То есть тот факт, что это можно сделать, не означает, что он или она должны это делать.

Внедрение зависимостей позволяет выполнять модульное тестирование, но также позволяет изменять поведение объекта без изменения кода этого объекта (принцип открытия / закрытия). Таким образом, это не просто тестируемый код, а гибкий код, который получается. Обычно я обнаружил, что между поддерживаемым / гибким кодом и тестируемым кодом существует сильная корреляция.

Эрик Дитрих
источник
3
Или Moles позволяет вам думать, что вы получили чистый, хорошо продуманный тестируемый код, потому что вы тестируете с помощью магии.
Уайетт Барнетт
@WyattBarnett Да, очень верно. У Крота есть удивительная способность заставить кого-то сказать: «Кому нужен весь этот полиморфизм и открытое / закрытое, во всяком случае?!?»
Эрик Дитрих
1
Кроты были заменены фальшивками , и со страницы фальшивки «Фреймворк Fakes помогает разработчикам создавать, поддерживать и внедрять фиктивные реализации в своих модульных тестах» Похоже на DI.
Знак
1
@Sign Ваша ссылка также гласит: «Платформа Fakes может использоваться для добавления любого метода .NET, включая не виртуальные и статические методы в закрытых типах». Тот факт, что структуры изоляции могут использоваться вместе с DI, не означает, что они являются DI.
Эрик Дитрих
4

Нет, DI не важен для модульного тестирования, но очень помогает.

Вы можете использовать фабрики или локаторы и тестировать, как если бы вы использовали DI (просто не так элегантно и потребовало бы больше настроек).

Кроме того, Mock Objects будет важен в устаревших системах, где многие вызовы делегируются функциям вместо зависимостей. (Ложные объекты также можно широко использовать при правильной настройке)

Могут быть настройки, когда тестирование практически невозможно. Но это не зависит от того, используется ли внедрение зависимостей.

smp7d
источник
3

Нет , внедрение зависимостей не является обязательным для модульного тестирования.

Внедрение зависимостей помогает, если у вас есть класс, которому нужен зависимый экземпляр класса для некоторой подпроцессорной обработки. Вместо DI вы можете разделить логику бизнес-метода на часть получения данных (которая не проверяется на единицу) и часть вычисления, которая может быть проверена на единицу.

Пример (с использованием DI) Эта реализация зависит от Employee, Account, ...

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     if (amount > 100 && employee.isStudent())
        return false;
     if (to.getOwner().getFamiliyName() == employee.getFamilyName() && ...
        return false; // cannot transfer money to himself;
     ...
 }

После разделения сбора данных и расчета:

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     return hasPermissionToTransferMoney(employee.isStudent(), employee.getFamilyName(), to.getOwner().getFamilyName(), ...);
 }

 // the actual permission calculation
 static bool hasPermissionToTransferMoney(boolean isStudent, string employeeFamilyName, string receiverFamilyName, ...)
     if (amount > 100 && isStudent)
        return false;
     if (receiverFamilyName == employeeFamiliyName && ...
        return false; // cannot transfer money to himself
     ...
 }

Расчетная часть может быть легко протестирована без внедрения зависимостей.

k3b
источник
1
  • Внедрение зависимостей не является обязательным для модульного тестирования

  • С другой стороны, инверсия управления необходима, если вы хотите поменять одну реализацию на другую.

CodeART
источник
0

Да, есть альтернативы использованию DI для изоляции.

Одна альтернатива состоит в том, чтобы использовать фабрики или ServiceLocator, который можно настроить из тестов, чтобы они возвращали фиктивные объекты вместо реальных.

Другим является использование подходящего изоляционного каркаса или насмешного инструмента. Такие инструменты существуют практически для каждого современного языка программирования (по крайней мере для Java, C #, Ruby и Python). Они могут изолировать тестируемый класс от реализации других классов / типов, даже если тестируемый класс непосредственно создает экземпляры своих зависимостей.

Рожерио
источник