Как динамически переключать/изменять testInstrumentationRunner с помощью gradle

В моем проекте две разные группы тестов. Одна группа работает только с по умолчанию AndroidJUnitRunner, другая должна запускаться с пользовательской реализацией TestRunner extends MonitoringInstrumentation.

В настоящее время я переключаю testInstrumentationRunner, редактируя build.gradle каждый раз, когда мне нужно запустить другую группу тестов:

android{
      defaultConfig {
          //testInstrumentationRunner "my.custom.TestRunner"
           testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
      }
}

Я знаю, что у флейворов может быть свой собственный testInstrumentationRunner, но мое текущее приложение уже имеет 2 flavourDimensions. Использование вкусов на самом деле предназначено для разных версий приложения. Мне нужны 2 версии тестового приложения, которые тестируют одно и то же приложение с разными testInstrumentationRunner s.

Я попытался изменить testInstrumentationRunner, выполнив итерацию по всем тестовым вариантам. На самом деле существует несколько свойств testInstrumentationRunner:

android.testVariants.all { TestVariant variant ->
    //readonly
    variant.variantData.variantConfiguration.instrumentationRunner

    variant.variantData.variantConfiguration.defaultConfig.testInstrumentationRunner

}

Но как только android.testVariants вызывается, сборка настроена, и все изменения не отражаются в сборке.

Как я могу изменить testInstrumentationRunner (из плагина gradle) динамически?

Я бы предпочел иметь две разные задачи gradle, каждая из которых использует другой testInstrumentationRunner, но тестирует один и тот же вариант. Поскольку я намерен создать плагин gradle, решение также должно работать как плагин.

Ответы

Ответ 1

С плагином android gradle 1.3 можно создавать отдельные тестовые модули. Каждый из этих тестовых модулей может иметь свой собственный testInstrumentationRunner.

Подробный пример приведен в примере проекта AndroidTestingBlueprint в github.

Решение от @johan-stuyts, получившего награду, отлично работает (или, по крайней мере, с плагином android gradle 1.2). Но он использует частные API, и создание отдельного модуля проще и надежнее.

Ответ 2

У меня была аналогичная проблема, я использовал gradle taskGraph. Основываясь на вашем заявлении "Мой проект имеет две разные группы тестов". Я собираюсь предположить, что у вас разные задачи определены, я назову их testGroupOne и testGroupTwo:

task testGroupOne{
}
task testGroupTwo{
}
gradle.taskGraph.whenReady {taskGraph ->
    if(taskGraph.hasTask(testGroupOne)){
        testInstrumentationRunner "my.custom.TestRunner"
    } else if (taskGraph.hasTask(testGroupTwo)){
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}

Это позволяет вам установить значение testInstrumentationRunner после настройки, но перед выполнением.

Ответ 3

Считаете ли вы использование параметра console в качестве переключателя между двумя конфигурациями? Проще всего:

android {
      defaultConfig {
           if (project.ext.has("customRunner")) {
               testInstrumentationRunner "my.custom.TestRunner"
           } else {
               testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
           }
      }
}

А затем, например, запустите gradlew aDeb -PcustomRunner, если вы хотите протестировать с помощью пользовательского бегуна или gradlew aDeb для использования по умолчанию.

Я знаю, что это не ракетостроение, но проще, правда? Вы также можете использовать его в своем плагине, просто получить объект Project и сделать аналогичную вещь.

Ответ 4

Это частичное решение, но, возможно, это сделает вещи немного менее громоздкими для вас: это не позволяет вам запускать тесты с обоими тестовыми бегунами в одной и той же сборке. Если вы хотите, чтобы вам пришлось клонировать все экземпляры задач DeviceProviderInstrumentTestTask, которые, на мой взгляд, слишком сложны и хрупки.

Это работает для меня (Gradle 2.4 и Android build tools 1.2.3). Он использует внутренние API-интерфейсы, поэтому возможно, что это больше не работает со следующей версией инструментов сборки Android.

Вы должны изменить задачи, сгенерированные подключаемым модулем Android после того, как проект был оценен. Затем изменения будут использоваться тестовыми задачами. Свойство, которое фактически изменяет используемый тестовый бегун, task.testVariantData.variantConfiguration.testedConfig.mergedFlavor.testInstrumentationRunner. Вместо того, чтобы делать эти изменения во время фазы конфигурации (то есть вне задачи), изменения применяются с использованием задачи, поэтому ваш тестовый бегун используется только по запросу. Заставляя тестовые задачи запускать после useMyTestRunner (если последний является частью сборки), имя класса тестового бегуна будет изменено при запуске тестовой задачи:

project.afterEvaluate {
    task useMyTestRunner << {
        tasks.withType(com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask.class) { task ->
            task.testVariantData.variantConfiguration.testedConfig.mergedFlavor.testInstrumentationRunner = 'com.mycompany.MyTestRunner'
        }
    }

    tasks.withType(com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask.class) { task ->
        task.mustRunAfter useMyTestRunner
    }
}

Теперь вы можете запускать тесты, используя тестовый бегун по умолчанию, настроенный для аромата (-ов):

gradle :myApp:connectedAndroidTest

Если вы хотите запустить тесты с помощью вашего тестового бегуна, используйте:

gradle :myApp:connectedAndroidTest :myApp:useMyTestRunner

Я не добавлял проверки для null для любого из свойств, полученных с помощью task.testVariantData.variantConfiguration.testedConfig.mergedFlavor.testInstrumentationRunner. Вы должны добавить их для надежности. Я думаю, что по крайней мере testedConfig требует внимания. См. getInstrumentationRunner() в https://android.googlesource.com/platform/tools/build/+/master/builder/src/main/java/com/android/builder/VariantConfiguration.java

Вы можете использовать импорт для com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask в верхней части вашего файла сборки, поэтому вам нужно использовать простое имя класса.