From e1cfb1aca9620abde867cbe288aee37ee59da735 Mon Sep 17 00:00:00 2001 From: Daniel Jette Date: Tue, 15 Sep 2026 17:20:24 -0400 Subject: [PATCH 1/2] Add a recipe for custom build types and product flavors [skip ci] Document how the Gradle plugin infers install tasks and package IDs, why flavored and custom build type projects fail with "Unable to find instrumentation info", and the four settings to configure together. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AgUU49S4wSWd7vhJS7kEmU --- .../recipes/23-build-types-and-flavors.md | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 docs/docs/recipes/23-build-types-and-flavors.md diff --git a/docs/docs/recipes/23-build-types-and-flavors.md b/docs/docs/recipes/23-build-types-and-flavors.md new file mode 100644 index 00000000..083b29dc --- /dev/null +++ b/docs/docs/recipes/23-build-types-and-flavors.md @@ -0,0 +1,128 @@ +--- +keywords: [build type, build variant, product flavor, flavors, productFlavors, installTask, installAndroidTestTask, applicationPackageId, testPackageId, Unable to find instrumentation info] +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Using custom build types and product flavors + +The Testify Gradle Plugin infers how to install and run your tests from your module's configuration. The inference assumes a `debug` build type and no product flavors. If your tests run against a custom build type, or your app has product flavors, tell the plugin which variant to use. + +## What the plugin infers + +| Setting | Inferred value | +|---|---| +| `installTask` | The first task, alphabetically, whose name matches `install…Debug` | +| `installAndroidTestTask` | The first task, alphabetically, whose name matches `install…DebugAndroidTest` | +| `applicationPackageId` | Your `applicationId`, plus the `applicationIdSuffix` of the `debug` build type | +| `testPackageId` | The inferred `applicationPackageId` followed by `.test` | + +The inferred package IDs don't include a product flavor's `applicationId` or `applicationIdSuffix`. With more than one flavor, the inferred install tasks belong to whichever flavor sorts first alphabetically. + +When the values don't match the variant you're testing, the tests fail to start, and the output contains an error like this: + +``` +INSTRUMENTATION_STATUS: Error=Unable to find instrumentation info for: ComponentInfo{com.example.app.test/androidx.test.runner.AndroidJUnitRunner} +``` + +## Configure all four settings together + +Setting only the install tasks isn't enough. Testify runs the tests with `adb shell am instrument`, which needs the package IDs of the installed APKs. Set all four values for the variant you're testing. + +For example, for an app with a `googleMock` product flavor tested on its `debug` build type: + + + + +```groovy +android { + defaultConfig { + applicationId "com.example.app" + } + flavorDimensions = ["backend"] + productFlavors { + googleMock { + dimension "backend" + applicationIdSuffix ".mock" + } + } +} + +testify { + installTask "installGoogleMockDebug" + installAndroidTestTask "installGoogleMockDebugAndroidTest" + applicationPackageId "com.example.app.mock" + testPackageId "com.example.app.mock.test" +} +``` + + + + +```kotlin +android { + defaultConfig { + applicationId = "com.example.app" + } + flavorDimensions += "backend" + productFlavors { + create("googleMock") { + dimension = "backend" + applicationIdSuffix = ".mock" + } + } +} + +testify { + installTask = "installGoogleMockDebug" + installAndroidTestTask = "installGoogleMockDebugAndroidTest" + applicationPackageId = "com.example.app.mock" + testPackageId = "com.example.app.mock.test" +} +``` + + + + +- **`installTask` and `installAndroidTestTask`** are task names only, such as `installGoogleMockDebug`, not task paths like `:app:installGoogleMockDebug`. The name is `install` followed by the flavor name and then the build type. +- **`applicationPackageId`** is the application ID of the APK under test, with every flavor and build type suffix applied. +- **`testPackageId`** is the application ID of the test APK. Unless you've set `testApplicationId`, it's `applicationPackageId` followed by `.test`. + +To test a custom build type instead of `debug`, set `testBuildType` in the `android` block and use that build type in the task names, for example `installStaging` and `installStagingAndroidTest`. + +## Check the configuration + +Print the settings the plugin will use: + +```shell-session +$ ./gradlew app:testifySettings + + … + installAndroidTestTask = installGoogleMockDebugAndroidTest + installTask = installGoogleMockDebug + … + targetPackageId = com.example.app.mock + testPackageId = com.example.app.mock.test + testRunner = androidx.test.runner.AndroidJUnitRunner + … +``` + +`testifySettings` prints `applicationPackageId` as `targetPackageId`. + +To see exactly which `adb` commands Testify runs, add `-Pverbose=true` to any Testify task: + +```shell-session +$ ./gradlew app:screenshotTest -Pverbose=true +``` + +## Nested modules + +`screenshotTest` and `screenshotRecord` depend on the install tasks, so the APKs are installed before the tests run. For a module nested inside another directory, such as `:feature:login`, the plugin may not find the install tasks, and it skips installing without an error ([#238](https://github.com/ndtp/android-testify/issues/238)). If your tests fail because the test APK isn't installed, run the install tasks yourself first: + +```shell-session +$ ./gradlew :feature:login:installGoogleMockDebug :feature:login:installGoogleMockDebugAndroidTest +$ ./gradlew :feature:login:screenshotTest +``` + +For Android library modules, see [Configuring Testify for Android Library Projects](21-library-projects.md). From 4c35d3498a36f78a965ac7be6f0db516098b0440 Mon Sep 17 00:00:00 2001 From: Daniel Jette Date: Mon, 21 Sep 2026 16:11:41 -0400 Subject: [PATCH 2/2] Recommend moduleName for nested modules [skip ci] --- .../recipes/23-build-types-and-flavors.md | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/docs/docs/recipes/23-build-types-and-flavors.md b/docs/docs/recipes/23-build-types-and-flavors.md index 083b29dc..2fb22f63 100644 --- a/docs/docs/recipes/23-build-types-and-flavors.md +++ b/docs/docs/recipes/23-build-types-and-flavors.md @@ -1,5 +1,5 @@ --- -keywords: [build type, build variant, product flavor, flavors, productFlavors, installTask, installAndroidTestTask, applicationPackageId, testPackageId, Unable to find instrumentation info] +keywords: [build type, build variant, product flavor, flavors, productFlavors, installTask, installAndroidTestTask, applicationPackageId, testPackageId, moduleName, nested module, Unable to find instrumentation info] --- import Tabs from '@theme/Tabs'; @@ -20,15 +20,17 @@ The Testify Gradle Plugin infers how to install and run your tests from your mod The inferred package IDs don't include a product flavor's `applicationId` or `applicationIdSuffix`. With more than one flavor, the inferred install tasks belong to whichever flavor sorts first alphabetically. -When the values don't match the variant you're testing, the tests fail to start, and the output contains an error like this: +When the install tasks or `testPackageId` don't match the variant you're testing, the tests fail to start. The message comes from Android rather than Testify, and you'll see an error like this: ``` INSTRUMENTATION_STATUS: Error=Unable to find instrumentation info for: ComponentInfo{com.example.app.test/androidx.test.runner.AndroidJUnitRunner} ``` +When `applicationPackageId` doesn't match, the tests can still run, but `screenshotPull` and `screenshotClear` look for screenshots in the wrong app. + ## Configure all four settings together -Setting only the install tasks isn't enough. Testify runs the tests with `adb shell am instrument`, which needs the package IDs of the installed APKs. Set all four values for the variant you're testing. +Setting only the install tasks isn't enough. Testify runs the tests with `adb shell am instrument` using `testPackageId`, and reads screenshots from the device using `applicationPackageId`. Set all four values for the variant you're testing. For example, for an app with a `googleMock` product flavor tested on its `debug` build type: @@ -118,7 +120,34 @@ $ ./gradlew app:screenshotTest -Pverbose=true ## Nested modules -`screenshotTest` and `screenshotRecord` depend on the install tasks, so the APKs are installed before the tests run. For a module nested inside another directory, such as `:feature:login`, the plugin may not find the install tasks, and it skips installing without an error ([#238](https://github.com/ndtp/android-testify/issues/238)). If your tests fail because the test APK isn't installed, run the install tasks yourself first: +`screenshotTest` and `screenshotRecord` depend on the install tasks, so the APKs are installed before the tests run. The plugin looks for the install tasks at `::`, and `moduleName` defaults to the module's own name, without its parent directories. For a module nested inside another directory, such as `:feature:login`, that gives `:login:installGoogleMockDebugAndroidTest`, which doesn't exist, so the plugin skips installing without an error ([#238](https://github.com/ndtp/android-testify/issues/238)). + +Set `moduleName` to the module's full path, without the leading colon: + + + + +```groovy +testify { + moduleName "feature:login" +} +``` + + + + +```kotlin +testify { + moduleName = "feature:login" +} +``` + + + + +The plugin then finds `:feature:login:installGoogleMockDebugAndroidTest` and installs the APKs before the tests run. Testify also uses `moduleName` in the commands it suggests in error messages, so those name the right module too. + +If the install tasks still aren't found, run them yourself before the tests: ```shell-session $ ./gradlew :feature:login:installGoogleMockDebug :feature:login:installGoogleMockDebugAndroidTest