Статьи

Библиотеки внедрения и тестирования зависимостей Android

Все, что делает жизнь проще для Android, должно быть изучено. Существует несколько библиотек, которые облегчают тестирование, увеличивают модульность вашего кода и предоставляют уже созданные фиктивные объекты:

  1. RoboGuice — это адаптация библиотеки внедрения зависимостей Google, но для Android
  2. Robolectric — это платформа / платформа для тестирования, которая устраняет необходимость постоянной насмешки над объектами Android. Это также работает с RoboGuice.

Я не могу начать благодарить парня, который впервые познакомил меня с внедрением зависимости. Исходя из мира C ++, где объекты не могут описать себя, и нет никакого отражения, если разработчик намеренно не реализует его сам, на более современный язык, я часто создавал свои собственные статические фабрики для имитации шаблона @inject. Но зачем изобретать колесо каждый раз, если объект может идеально описать себя виртуальной машине?

Если вы никогда раньше не видели Dependency Injection (DI), посмотрите «простой пример», который RoboGuice предоставляет на своей веб-странице. На первый взгляд это может показаться причудливым способом очистки кода, чтобы сопровождающие могли сосредоточиться только на специфике действий в каждом методе. Это побочная выгода (очень хорошая), но только побочная. Пример не демонстрирует истинную силу такой структуры в тестировании и утверждении кода без ошибок.

Без DI в вашей кодовой базе

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public abstract class WidgetBase implements IWidget{
   protected ISomeObject mSomeObject;
 
   public WidgetBase(ISomeObject _obj){
      mSomeObject = _obj;
   }
}
 
public AlertWidgetFactory{
   static private WidgetBase mTest = null;
   static private boolean mTestFlag = false;
   static WidgetBase create(ISomeObject _obj){
      if(mTestFlag){
         return mTest;
      }
      return new AlertWidget(_obj);
   }
 
   static void setTest(WidgetBase _testWidget){
      mTest = _testWidget;
   }
 
   static void setTestFlag(boolean _flag){
      mTestFlag = _false;
   }
}

Теперь, когда я приступаю к тестированию своего кода, я могу автоматизировать все. Во время фазы «setUp» моего модульного теста JUnit я могу заменить какую-то смоделированную версию «WidgetBase», чтобы она возвращала много возможных записей пользователя. Посмотрите, как легко проверить это делает следующий код:

1
2
3
4
5
6
7
public void someMethod(String _value){
    if(_value == ""){
      ISomeObject foo = new SomeValueAssigner( _value );
      IWidget alert = AlertWidgetFactory.create(foo);
   }
   //more code...
}

Конечно, если у меня есть другой тип класса, который я хочу создать на фабрике, мне придется кодировать фабрику только для этого объекта. (В C ++ я бы просто шаблонировал его и имел только один класс для фабрики. Иногда шаблоны — это хорошо , не так ли?

С DI в вашем коде

Некоторые могут кричать на меня за то, что я не использую простой интерфейс вместо абстрактного класса. Был пункт к этому. Интерфейсы не могут описать конструктор, и я хотел, чтобы мой объект принял параметры в конструкторе. Но почему?

Для начала рассмотрим приведенный выше код. Вы заметили, что у меня есть жестко запрограммированная зависимость в SomeValueAssigner? На самом деле я не могу выполнить модульное тестирование функции «someMethod», так как не могу удалить взаимозависимости, предварительно не создав для этого заводской метод! О человечество. Сколько фабричных объектов нам нужно будет создать, чтобы протестировать кодовую базу приличного размера? Каждый раз, когда я хочу новый объект, мне, возможно, придется создавать для него интерфейс, абстрактный базовый класс и / или объект фабрики. Мне также может понадобиться добавить новый метод к объекту фабрики для каждого отдельного конструктора.

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

Как избежать этапа эмуляции

Разве вы не знаете, но тестирование компонентов пользовательского интерфейса — тяжелая работа. Нам нужно не только смотреть на вещи в первую очередь, просто чтобы убедиться, что все формы / размеры / цвета отображаются правильно, но мы также должны убедиться, что они делают то, что должны делать. Затем наступает период ожидания, пока эмулируемое устройство Android прогревается. Цитирование с сайта Robolectric:

Запуск тестов на эмуляторе Android или устройстве идет медленно! Сборка, развертывание и запуск приложения часто занимают минуту или больше. Это не способ сделать TDD. Должен быть лучший способ.

Я не мог согласиться больше. Ожидание эмулятора для компиляции и загрузки приложения для небольшого, крошечного исправления является болезненным.
Robolectric позволяет итерировать быстрее. Быстрая итерация приводит к лучшему дизайну. Лучший дизайн приводит к меньшему количеству проблем при обслуживании. Меньше проблем с обслуживанием приводит к большему времени для добавления новых функций или улучшения функциональности. Все это приводит к улучшению качества продукта для конечного пользователя.
Это чрезвычайно полезно для вашей прибыли. Несмотря на то, что этот раздел очень короткий, он почти обратно пропорционален тому, сколько времени он вас спасет.

Выводы

RoboGuice позволяет вам тестировать проще и добавляет еще один инструмент / идиому, помогающий отделить ваш код. Robolectric позволяет тестировать быстрее. Соедините их вместе, и вы получите среду, в которой вы работаете более эффективно и продуктивно. Это две библиотеки, без которых вам не обойтись.

Ссылка: Android: библиотеки, которые вы должны использовать от нашего партнера JCG в блоге со статической типизацией .

Статьи по Теме :