Intereting Posts
ContentResolver.insert возвращает null Android: отображение меню в нескольких действиях Захват нажатия клавиш, когда телефон Android работает в глубоком спящем режиме Как увидеть фактические параметры gcc при создании Android из источника? Локализация строковых ресурсов, добавленных через build.gradle с использованием функции resValue, Эта библиотека поддержки не должна использовать другую версию Error in build.gradle Android Throwing OutOfMemoryError «Не удалось выделить выделение по 164 байта с 44 свободными байтами и 44B до OOM» (рекурсивный случай) Как я могу предотвратить «убить» приложение или службу Android из диспетчера задач? Получение текущего экземпляра фрагмента в viewpager Выберите первое устройство из сопряженных устройств Список посылок для списка объектов Push-уведомления в платформе Android Java.lang.IllegalStateException: активность была уничтожена OnReceiver of BroadcastReceiver не вызван, AlarmManager Как просмотреть список приложений и приложений в Google Play с помощью некоторых API?

Как Dagger 2 упрощает тестирование на Android?

Одно из лучших преимуществ использования DI заключается в том, что тестирование намного проще ( что такое инъекция зависимостей ) тоже поддерживает его. Большинство инфраструктур DI, с которыми я работал на других языках программирования ( MEF на .NET , Typhoon на Obj-C / Swift , Laravel IoC Container на PHP и некоторые другие), позволяет разработчику регистрировать зависимости на одной точке входа Для каждого компонента, тем самым предотвращая «создание» зависимости от самого объекта.

После того, как я прочитал документацию Dagger 2 , это звучит великолепно для всего бизнеса «без отражений», но я не вижу, как это облегчает тестирование, поскольку объекты все еще создают собственные зависимости.

Например, в примере CoffeMaker:

public class CoffeeApp { public static void main(String[] args) { // THIS LINE CoffeeShop coffeeShop = DaggerCoffeeShop.create(); coffeeShop.maker().brew(); } } 

Несмотря на то, что вы явно не вызываете new , вам все равно придется создавать свою зависимость.

Теперь для более подробного примера перейдем к примеру Android . Если вы DemoActivity класс DemoActivity , вы заметите, что реализация onCreate выполняется следующим образом:

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Perform injection so that when this call returns all dependencies will be available for use. ((DemoApplication) getApplication()).component().inject(this); } 

Вы можете ясно видеть, что нет развязки с компонентом DI, а также фактическим кодом. Итак, вам нужно будет mock / stub ((DemoApplication) getApplication()).component().inject(this); На тестовом примере (если это возможно).

До этого момента я знаю, что Dagger 2 довольно популярен, поэтому мне нужно что-то увидеть. Итак, как Dagger 2 упрощает тестирование классов? Как бы я высмеивал, скажем, сетевой сервис-класс, от которого зависит моя активность? Я бы хотел, чтобы ответ был как можно более простым, поскольку меня интересует только тестирование.

Кинжал 2 не облегчает тестирование

… заставляя вас в первую очередь вводить зависимости, что, естественно, делает отдельные классы более проверяемыми.

Последнее, что я слышал, команда Dagger 2 все еще рассматривала потенциальные подходы к улучшению поддержки тестирования – хотя любые обсуждения продолжаются, они, похоже, не очень популярны.

Итак, как я могу проверить сейчас?

Вы правильно указываете, что классы, которые хотят явно использовать компонент, зависят от него. Так что … вводите эту зависимость! Вам придется вводить компонент «вручную», но это не должно быть слишком сложной задачей.

Официальный путь

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

 public class CoffeeApp { public static CoffeeShop sCoffeeShop; public static void main(String[] args) { if (sCoffeeShop == null) { sCoffeeShop = DaggerCoffeeShop.create(); } coffeeShop.maker().brew(); } } // Then, in your test code you inject your test Component. CoffeeApp.sCoffeeShop = DaggerTestCoffeeShop.create(); 

Этот подход хорошо подходит для вещей, которые вы всегда хотите заменить при выполнении тестов – например, код сети, в котором вы хотите запускать вместо макетного сервера, или реализации IdlingResource для выполнения тестов Espresso.

Неофициальный путь

К сожалению, официальным способом может быть множество шаблонов кода – отлично, как одноразовая, но настоящая боль, если вы хотите только заменить одну зависимость для одного конкретного набора тестов.

Моим любимым взломом для этого является простое расширение любого @Provides модуля, который вы хотите заменить, а затем переопределить метод @Provides . Вот так:

 CoffeeApp.sCoffeeShop = DaggerCoffeeShop.builder() .networkModule(new NetworkModule() { // Do not add any @Provides or @Scope annotations here or you'll get an error from Dagger at compile time. @Override public RequestFactory provideRequestFactory() { return new MockRequestFactory(); } }) .build(); 

Проверьте этот смысл для полного примера.

«Позволяет разработчику регистрировать зависимости от одной точки входа для каждого компонента» – аналоги в кинжале 2 – это Module и Component которых вы определяете зависимости. Преимущество заключается в том, что вы не определяете зависимости непосредственно в своем компоненте, таким образом, развязывая его, поэтому позже при написании модульных тестов вы можете переключить component Dagger 2 на тестовый.

«Звучит здорово, что весь« не отраженный »бизнес» – вещь «без размышлений» не является «большой проблемой» в отношении кинжала. «Большая сделка» – это полная проверка графика зависимостей во время компиляции. Другие рамки DI не имеют этой функции, и если вы не можете определить, как выполняется какая-либо зависимость, вы получите сообщение об ошибке во время выполнения. Если ошибка находится в некотором редко используемом кодедже, ваша программа может выглядеть так, как будто она правильная, но в какой-то момент она не сработает.

«Даже если вы явно не называете новое, вам все равно нужно создать свою зависимость». – Ну, вам всегда нужно как-то инициировать инъекцию зависимости. Другой DI может «скрыть» / автоматизировать эту деятельность, но в конце где-то выполняется построение графика. Для кинжала 1 и 2 это делается при запуске приложения. Для «обычных» приложений (как показано в примере) в main() , «Приложения для Android» – в классе Application .

«Вы можете четко видеть, что нет развязки от компонента DI, до фактического кода» – Да, вы на 100% правильны. Это связано с тем, что вы не контролируете непосредственно жизненный цикл действий, фрагментов и сервисов в Android, то есть ОС создает эти объекты для вас, и ОС не знает, что вы используете DI. Вам нужно вручную ввести свои действия, фрагменты и услуги. Поначалу это кажется неудобным, но в реальной жизни единственной проблемой является то, что иногда вы можете забыть ввести свою активность в onCreate() и получить NPE во время выполнения.