Intereting Posts
BroadcastReceiver для CONNECTIVITY_ACTION всегда возвращает null в aim.getExtras () Интеграция Facebook в приложение для Android Android: PendingIntent from Notification не запускает onCreate (), если возвращает активность на экран Сделать определенную деятельность как корневую активность в стеке Как уменьшить размер веб-виджета динамически в соответствии с его содержимым? Couchbase Lite на Android L Проверьте, является ли строка числом Как избежать добавления метода инъекции для каждого представления? Android: войдите на сайт и сохраните сеанс / файл cookie, используя DefaultHttpClient Виджет Android не отображается в списке после Обработка изменений идентификатора регистрации в Google Cloud Messaging на Android Com.jayway.maven.plugins.android.generation2: Выполнение плагина не распространяется на конфигурацию жизненного цикла? Вызов функции javascript в Android WebView Проблема с блокировкой пользователя в списке чатов с использованием сервера smack и open fire Использовать RxAndroid или RxKotlin при программировании в Kotlin для Android?

Тесты для Android с помощью кинжала 2

У меня есть приложение для Android, которое использует Dagger 2 для инъекции зависимостей. Я также использую последние инструменты построения градации, которые позволяют вариант сборки для модульного тестирования и один для контрольных тестов. Я использую java.util.Random в своем приложении, и я хочу издеваться над этим для тестирования. В классах, которые я тестирую, не используются никакие вещи Android, поэтому они просто обычные классы Java.

В моем основном коде я определяю Component в классе, который расширяет класс Application , но в модульных тестах я не использую Application . Я попытался определить тестовый Module и Component , но Кинжал не будет генерировать Component . Я также попытался использовать Component который я определил в своем приложении, и обменивать Module при его создании, но Component приложения не имеет методов inject для моих тестовых классов. Как я могу предоставить примерную реализацию Random для тестирования?

Вот пример кода:

Заявка:

 public class PipeGameApplication extends Application { private PipeGame pipeGame; @Singleton @Component(modules = PipeGameModule.class) public interface PipeGame { void inject(BoardFragment boardFragment); void inject(ConveyorFragment conveyorFragment); } @Override public void onCreate() { super.onCreate(); pipeGame = DaggerPipeGameApplication_PipeGame.create(); } public PipeGame component() { return pipeGame; } } 

Модуль:

 @Module public class PipeGameModule { @Provides @Singleton Random provideRandom() { return new Random(); } } 

Базовый класс для тестов:

 public class BaseModelTest { PipeGameTest pipeGameTest; @Singleton @Component(modules = PipeGameTestModule.class) public interface PipeGameTest { void inject(BoardModelTest boardModelTest); void inject(ConveyorModelTest conveyorModelTest); } @Before public void setUp() { pipeGameTest = DaggerBaseModelTest_PipeGameTest.create(); // Doesn't work } public PipeGameTest component() { return pipeGameTest; } } 

или:

 public class BaseModelTest { PipeGameApplication.PipeGame pipeGameTest; // This works if I make the test module extend // the prod module, but it can't inject my test classes @Before public void setUp() { pipeGameTest = DaggerPipeGameApplication_PipeGame.builder().pipeGameModule(new PipeGameModuleTest()).build(); } public PipeGameApplication.PipeGame component() { return pipeGameTest; } } 

Испытательный модуль:

 @Module public class PipeGameTestModule { @Provides @Singleton Random provideRandom() { return mock(Random.class); } } 

Solutions Collecting From Web of "Тесты для Android с помощью кинжала 2"

В настоящее время это невозможно с помощью Dagger 2 (начиная с версии 2.0) без каких-либо обходных решений. Вы можете прочитать об этом здесь .

Подробнее о возможных методах обхода:

  • Как вы переопределяете модуль / зависимость в модульном тесте с Dagger 2.0?

  • Создание тестовых зависимостей при использовании Dagger2

Вы ударили ноготь по голове, сказав:

Компонент приложения не имеет методов ввода для моих тестовых классов

Итак, чтобы обойти эту проблему, мы можем сделать тестовую версию вашего класса Application. Тогда у нас может быть тестовая версия вашего модуля. И чтобы все это выполнялось в тесте, мы можем использовать Robolectric.

1) Создайте тестовую версию своего класса приложения

 public class TestPipeGameApp extends PipeGameApp { private PipeGameModule pipeGameModule; @Override protected PipeGameModule getPipeGameModule() { if (pipeGameModule == null) { return super.pipeGameModule(); } return pipeGameModule; } public void setPipeGameModule(PipeGameModule pipeGameModule) { this.pipeGameModule = pipeGameModule; initComponent(); }} 

2) Ваш исходный класс приложения должен иметь методы initComponent () и pipeGameModule ()

 public class PipeGameApp extends Application { protected void initComponent() { DaggerPipeGameComponent.builder() .pipeGameModule(getPipeGameModule()) .build(); } protected PipeGameModule pipeGameModule() { return new PipeGameModule(this); }} 

3) Ваш PipeGameTestModule должен расширять производственный модуль конструктором:

 public class PipeGameTestModule extends PipeGameModule { public PipeGameTestModule(Application app) { super(app); }} 

4) Теперь, в методе setup () вашего теста junit, установите этот тестовый модуль в тестовом приложении:

 @Before public void setup() { TestPipeGameApp app = (TestPipeGameApp) RuntimeEnvironment.application; PipeGameTestModule module = new PipeGameTestModule(app); app.setPipeGameModule(module); } 

Теперь вы можете настроить свой тестовый модуль, как вы хотели.

По-моему, вы можете подойти к этой проблеме, взглянув на нее под другим углом. Вы легко сможете провести тестирование вашего класса, не зависимо от кинжала для тестируемого строительного класса, с его издеваемыми зависимостями, введенными в него.

Я хочу сказать, что в тестовой настройке вы можете:

  • Макет зависимостей тестируемого класса
  • Построить класс под тест вручную, используя издевательства зависимостей

Нам не нужно проверять правильность ввода зависимостей, поскольку Dagger проверяет правильность графика зависимости во время компиляции. Таким образом, любые такие ошибки будут сообщены в результате сбоя компиляции. И именно поэтому должно быть приемлемым ручное создание испытуемого класса в методе установки.

Пример кода, в котором зависимость вводится с использованием конструктора в тестируемом классе:

 public class BoardModelTest { private BoardModel boardModel; private Random random; @Before public void setUp() { random = mock(Random.class); boardModel = new BoardModel(random); } @Test ... } public class BoardModel { private Random random; @Inject public BoardModel(Random random) { this.random = random; } ... } 

Пример кода, в котором зависимость вводится с использованием поля в тестируемом классе (в случае, если BoardModel сконструирован каркасом):

 public class BoardModelTest { private BoardModel boardModel; private Random random; @Before public void setUp() { random = mock(Random.class); boardModel = new BoardModel(); boardModel.random = random; } @Test ... } public class BoardModel { @Inject Random random; public BoardModel() {} ... } 

Если вы используете dagger2 с Android, вы можете использовать аппетитные приложения для издевательств.

См. Здесь демонстрацию вкусов при макетном тестировании (без кинжала): https://www.youtube.com/watch?v=vdasFFfXKOY

Эта кодовая база имеет пример: https://github.com/googlecodelabs/android-testing

В вашем /src/prod/com/yourcompany/Component.java вы предоставляете свои производственные компоненты.

В вашем /src/mock/com/yourcompany/Component.java вы предоставляете свои издевательские компоненты.

Это позволяет создавать сборки вашего приложения с или без насмешек. Он также позволяет параллельную разработку (backend одной командой, приложение frontend другой командой), вы можете высмеивать до тех пор, пока не будут доступны api-методы.

Как выглядят мои команды gradle (его Makefile):

 install_mock: ./gradlew installMockDebug install: ./gradlew installProdDebug test_unit: ./gradlew testMockDebugUnitTest test_integration_mock: ./gradlew connectedMockDebugAndroidTest test_integration_prod: ./gradlew connectedProdDebugAndroidTest 

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

Создайте аналогичный класс в своем модуле приложения:

 public class ActivityTest<T extends ViewModelBase> { @Inject public T vm; } 

Затем в приложении AppComponent добавьте:

 void inject(ActivityTest<LoginFragmentVM> activityTest); 

Тогда вы сможете ввести это в свой тестовый класс.

  public class HelloWorldEspressoTest extends ActivityTest<LoginFragmentVM> { @Rule public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class); @Test public void listGoesOverTheFold() throws InterruptedException { App.getComponent().inject(this); vm.email.set("1234"); closeSoftKeyboard(); } }