---
title: TestingBot Gradle Plugin for Espresso tests.
description: Upload Android apps and run Espresso tests on TestingBot's real device
  cloud straight from your Gradle build with the TestingBot Gradle plugin.
source_url:
  html: https://testingbot.com/support/integrations/ci-cd/gradle
  md: https://testingbot.com/support/integrations/ci-cd/gradle.md
---

# Gradle Plugin

We maintain a [Gradle](https://gradle.org/) plugin that uploads your Android app to [TestingBot Storage](https://testingbot.com/support/app-automate/help/upload) and runs [Espresso](https://testingbot.com/support/app-automate/espresso) tests on TestingBot's real device cloud, straight from your build, with no `curl` scripts required.

- `testingbotUpload` uploads an APK to TestingBot Storage and returns a reusable `tb://<appkey>` identifier. 
- `testingbotEspresso` uploads your app and instrumented test APK, runs Espresso across one or more devices, waits for the results, downloads a JUnit XML report, and fails the build on test failures. 
- `testingbotDevices` lists the Android devices available on your account. 
- Reads credentials from the DSL, Gradle properties or environment variables, and is configuration-cache compatible with no extra HTTP dependencies on your build classpath. 

The plugin is published on the [Gradle Plugin Portal](https://plugins.gradle.org/plugin/com.testingbot.gradle) and the source is available on [GitHub](https://github.com/testingbot/testingbot-gradle-plugin).

## Requirements

- Gradle 8.0 or newer, running on JDK 17 or newer.
- A TestingBot account. Grab your **key** and **secret** from the [member area](https://testingbot.com/members/user/edit).

## 1. Installation

Apply the plugin in your Android app module's build file.

```kotlin
// build.gradle.kts
plugins {
    id("com.testingbot.gradle") version "0.1.0"
}
```

Using the Groovy DSL:

```groovy
// build.gradle
plugins {
    id 'com.testingbot.gradle' version '0.1.0'
}
```

## 2. Credentials

Credentials are resolved in this order, first match wins:

- The DSL: `testingbot { key.set("…"); secret.set("…") }`
- Gradle properties: `-Ptestingbot.key=… -Ptestingbot.secret=…` (or in `gradle.properties`)
- Environment variables: `TESTINGBOT_KEY` / `TESTINGBOT_SECRET`

In CI, prefer environment variables backed by secrets. Never commit credentials to your repository.

## 3. Usage

Configure the `testingbot { }` block with your APKs and the device capabilities you want to run on.

```kotlin
testingbot {
    // Credentials (optional here if provided via env vars / gradle properties)
    key.set(providers.environmentVariable("TESTINGBOT_KEY"))
    secret.set(providers.environmentVariable("TESTINGBOT_SECRET"))

    // The app + instrumented test APKs
    appApk.set(layout.projectDirectory.file("app/build/outputs/apk/debug/app-debug.apk"))
    testApk.set(
        layout.projectDirectory.file("app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk")
    )

    // One run is started per capability. Keys are passed through to TestingBot verbatim.
    capabilities.set(
        listOf(
            mapOf("deviceName" to "Pixel.*", "version" to "14", "platformName" to "Android", "realDevice" to true),
            mapOf("deviceName" to "Galaxy S.*", "version" to "13", "platformName" to "Android")
        )
    )

    // Optional behavior
    waitForResults.set(true) // poll until the run completes (default true)
    failBuildOnTestFailure.set(true) // default true
    reportXml.set(layout.buildDirectory.file("testingbot/espresso-junit.xml"))
}
```

`deviceName` accepts a regular expression, so `"Pixel.*"` matches any Pixel and `"*"` matches any device. Commonly used keys are `deviceName`, `version`, `platformName` (`"Android"`), `realDevice`, `phoneOnly` and `tabletOnly`. Run `./gradlew testingbotDevices` to see what is available on your plan.

## 4. Tasks

```bash
# Upload only
./gradlew testingbotUpload

# Build the APKs first, then run Espresso on TestingBot
./gradlew assembleDebug assembleDebugAndroidTest testingbotEspresso

# Discover device names/versions for your capabilities
./gradlew testingbotDevices
```

## 5. Android Gradle Plugin auto-wiring

If the `com.android.application` plugin is applied, naming a variant lets the plugin fill in the conventional APK paths and depend on the matching `assemble` tasks automatically.

```kotlin
testingbot {
    autoWireFromVariant.set("debug") // defaults appApk/testApk + dependsOn assembleDebug(AndroidTest)
    capabilities.set(listOf(mapOf("deviceName" to "Pixel.*", "version" to "14", "platformName" to "Android")))
}
```

Now `./gradlew testingbotEspresso` builds the APKs and runs the tests in one go. Explicitly set `appApk` / `testApk` paths always take precedence.

## Full DSL reference

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `key` | `String` | `TESTINGBOT_KEY` / `testingbot.key` | API key |
| `secret` | `String` | `TESTINGBOT_SECRET` / `testingbot.secret` | API secret |
| `apiBaseUrl` | `String` | `https://api.testingbot.com/v1` | API base URL |
| `appApk` | `RegularFile` | — | App APK to upload / test |
| `testApk` | `RegularFile` | — | Instrumented Espresso test APK |
| `capabilities` | `List<Map<String, Any>>` | `[]` | One run per entry; passed through verbatim |
| `espressoOptions` | `Map<String, Any>` | `{}` | Optional run options passed through verbatim |
| `waitForResults` | `Boolean` | `true` | Poll until the run completes |
| `pollIntervalSeconds` | `Long` | `15` | Seconds between status polls |
| `timeoutMinutes` | `Long` | `30` | Max minutes to wait for results |
| `failBuildOnTestFailure` | `Boolean` | `true` | Fail the build when a run reports failures |
| `reportXml` | `RegularFile` | `build/testingbot/espresso-junit.xml` | Where to write the JUnit report |
| `autoWireFromVariant` | `String` | — | Optional AGP variant to auto-wire |

## CI example (GitHub Actions)

```yaml
- uses: actions/setup-java@v4
  with: { distribution: temurin, java-version: 21 }
- run: ./gradlew assembleDebug assembleDebugAndroidTest testingbotEspresso
  env:
    TESTINGBOT_KEY: ${{ secrets.TESTINGBOT_KEY }}
    TESTINGBOT_SECRET: ${{ secrets.TESTINGBOT_SECRET }}
```

### Looking for more help?

Have questions or need more information? Reach out via email or Slack.

[Email us](https://testingbot.com/contact/new) [Join our Slack](https://join.slack.com/t/testingb0t/shared_invite/zt-3bcw9xch-jk19~6XPs_xBrsAgAedkCw)
