Getting started
Build your app and androidTest APKs, get your access key, and run your first Espresso build on a real Android phone with four curl calls.
Four calls: upload the app, upload the test suite, start a build, read the result. This page does each one with curl, so you can paste it into any CI step.
1. Build the two APKs
From your Android project, the same Gradle tasks you already use for connectedAndroidTest:
./gradlew assembleDebug assembleDebugAndroidTestThat gives you two files:
| File | What it is |
|---|---|
app/build/outputs/apk/debug/app-debug.apk | The app — what your tests exercise |
app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk | The test suite — your Espresso tests and their runner |
Both must be ordinary .apk files signed with the same key, which is what Gradle's debug build does for you. See Uploading APKs for what we accept.
2. Get your access key
Open API Keys in your dashboard and copy your key. It starts with xyz_.
That key is the only thing that authenticates you. Send it either way:
- HTTP Basic — the key is the password:
curl -u "x:$TESTKASE_ACCESS_KEY" …. Thexis a placeholder username we do not check. - Bearer —
Authorization: Bearer $TESTKASE_ACCESS_KEY.
It is the same key your Appium and Selenium tests use. Keep it in your CI's secret store; the examples below read it from TESTKASE_ACCESS_KEY.
A missing, wrong or expired key always answers 401 with "That access token isn't valid."
3. Upload the app
curl -u "x:$TESTKASE_ACCESS_KEY" \
-F "file=@app/build/outputs/apk/debug/app-debug.apk" \
https://hub.testkase.com/espresso/v1/app{
"app_url": "farm://app-3f9c01a2b4c5d6e7",
"app_name": "app-debug",
"package": "com.acme",
"expires_at": "2027-01-07T09:12:44.000Z"
}Keep app_url. package is read from the APK itself.
This upload must finish within five minutes. For a large APK or a slow CI connection, use the direct upload instead. It sends the file straight to storage and gives you the same app_url.
4. Upload the test suite
curl -u "x:$TESTKASE_ACCESS_KEY" \
-F "file=@app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk" \
https://hub.testkase.com/espresso/v1/test-suite{
"test_suite_url": "farm://suite-81ad02c4e9f1b367",
"test_package": "com.acme.test",
"target_package": "com.acme",
"runner": "androidx.test.runner.AndroidJUnitRunner",
"expires_at": "2027-01-07T09:13:02.000Z"
}Keep test_suite_url. target_package must be your app's package, or the build in the next step is refused.
5. Start a build
Name the devices as "<device name>-<Android version>", for example "Pixel 8-17" (device names and Android versions vary — copy them from the capability generator). The capability generator lists the Android devices and versions we have right now.
curl -u "x:$TESTKASE_ACCESS_KEY" \
-H "Content-Type: application/json" \
https://hub.testkase.com/espresso/v1/build \
-d '{
"app": "farm://app-3f9c01a2b4c5d6e7",
"testSuite": "farm://suite-81ad02c4e9f1b367",
"devices": ["Pixel 8-17", "OnePlus Nord 6-16"],
"buildTag": "nightly-412"
}'{ "build_id": "5b1e7c2a-9d4f-4e1b-8a63-2f0c7d9e4b18", "message": "Success" }Everything is checked before any device is taken: a wrong URL, a suite built for another app or a device name we don't have is a 400 straight away, and nothing is billed. Every field is on Starting a build.
6. Read the result
curl -u "x:$TESTKASE_ACCESS_KEY" \
https://hub.testkase.com/espresso/v1/builds/5b1e7c2a-9d4f-4e1b-8a63-2f0c7d9e4b18{
"build_id": "5b1e7c2a-9d4f-4e1b-8a63-2f0c7d9e4b18",
"status": "running",
"project": null,
"buildTag": "nightly-412",
"devices": [
{ "device": "Pixel 8-17", "sessionId": "0747163a-5e2d-4c8b-9f10-6a3b2c1d0e9f", "status": "running",
"tests": { "total": 3, "passed": 2, "failed": 0, "skipped": 1 } },
{ "device": "OnePlus Nord 6-16", "sessionId": "e6ceae40-1b7c-4d2e-8f3a-9c0b5d6e7f81", "status": "passed",
"tests": { "total": 3, "passed": 2, "failed": 0, "skipped": 1 } }
]
}Poll it until status is no longer queued or running, then gate your CI on it: passed is green, anything else is not. What each status means is on Results.
A simple poll loop for a CI step:
BUILD_ID=5b1e7c2a-9d4f-4e1b-8a63-2f0c7d9e4b18
while true; do
STATUS=$(curl -s -u "x:$TESTKASE_ACCESS_KEY" \
"https://hub.testkase.com/espresso/v1/builds/$BUILD_ID" | jq -r .status)
echo "build $BUILD_ID: $STATUS"
case "$STATUS" in queued|running) sleep 30 ;; *) break ;; esac
done
[ "$STATUS" = "passed" ]7. See it in the dashboard
Open Automate → Mobile Apps → Runs. Your build is listed under its buildTag (or its project if you sent no tag, or "Espresso build" if you sent neither). Open a device's session for the per-test video, the logs and the All Commands tab.
The status response does not include a dashboard link, so a recognisable buildTag — a CI run number or commit SHA — is the easiest way to find it.
Next
- Uploading APKs — what we check, limits, deleting old uploads
- Starting a build — emulators, filters, timeouts
- Results — per-test stack traces, video and logs
Espresso on real devices
Upload the two APKs Gradle already builds — your app APK and its androidTest APK — and run your Espresso tests on our real Android phones and emulators, from CI, with no change to your tests.
Uploading APKs
Upload your app APK and your androidTest APK, and get the two farm:// URLs a build needs.
