Sauce Labs Mobile Testing: Setup and Best Practices
Sauce Labs provides cloud-based mobile testing on real iOS and Android devices with a focus on Appium integration and enterprise-grade reliability. This guide covers setup, configuration, and best practices for running your mobile tests on Sauce Labs.
Sauce Labs Mobile Testing Platforms
Sauce Labs offers two mobile testing platforms:
Sauce Labs Real Device Cloud: Physical iOS and Android devices for E2E testing, exploratory testing, and debugging.
Sauce Labs Virtual Devices: Android emulators and iOS simulators for faster, cheaper execution — good for unit and integration tests.
The choice mirrors the broader real device vs. emulator decision: real devices for production-confidence testing, virtual devices for high-frequency CI.
Getting Started
You'll need:
- Sauce Labs account (free trial available)
SAUCE_USERNAMEandSAUCE_ACCESS_KEYfrom your account settings- Your app binary (
.apkfor Android,.ipafor iOS) - Appium tests written in your framework of choice
Step 1: Upload Your App
Apps can be uploaded to Sauce Labs storage for use in tests.
Using the REST API:
curl -u "$SAUCE_USERNAME:<span class="hljs-variable">$SAUCE_ACCESS_KEY" \
-X POST <span class="hljs-string">"https://api.us-west-1.saucelabs.com/v1/storage/upload" \
-H <span class="hljs-string">"Content-Type: application/octet-stream" \
-H <span class="hljs-string">"Content-Disposition: attachment; filename=app.apk" \
--data-binary @app/build/outputs/apk/debug/app-debug.apkResponse:
{
"item": {
"id": "a1b2c3d4-...",
"storage_path": "storage:filename=app-debug.apk"
}
}Use storage:filename=app-debug.apk as the app capability in your tests.
Using the Sauce CLI (saucectl):
npm install -g saucectl
saucectl storage upload app-debug.apkStep 2: Configure Appium Capabilities
Sauce Labs uses W3C capabilities with sauce:options for Sauce-specific configuration:
Android real device:
const capabilities = {
platformName: 'Android',
'appium:app': 'storage:filename=app-debug.apk',
'appium:deviceName': 'Samsung Galaxy S24',
'appium:platformVersion': '14',
'appium:automationName': 'UiAutomator2',
'sauce:options': {
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY,
deviceType: 'phone',
appiumVersion: '2.0.0',
name: 'Login Flow Test',
build: `Build ${process.env.BUILD_NUMBER}`,
tags: ['regression', 'android'],
privateDevicesOnly: false, // Set true for dedicated devices
carrierConnectivityEnabled: false
}
};iOS real device:
const capabilities = {
platformName: 'iOS',
'appium:app': 'storage:filename=app.ipa',
'appium:deviceName': 'iPhone 15 Pro',
'appium:platformVersion': '17',
'appium:automationName': 'XCUITest',
'sauce:options': {
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY,
name: 'iOS Login Test',
build: `Build ${process.env.BUILD_NUMBER}`,
tags: ['regression', 'ios']
}
};Virtual device (Android emulator):
const capabilities = {
platformName: 'Android',
'appium:app': 'storage:filename=app-debug.apk',
'appium:deviceName': 'Android GoogleAPI Emulator',
'appium:platformVersion': '12.0',
'appium:automationName': 'UiAutomator2',
'sauce:options': {
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY,
name: 'Emulator Test',
build: `Build ${process.env.BUILD_NUMBER}`
}
};Step 3: Connect to Sauce Labs
Replace your local Appium server URL with the Sauce Labs endpoint:
const driver = await remote({
protocol: 'https',
hostname: 'ondemand.us-west-1.saucelabs.com',
port: 443,
path: '/wd/hub',
capabilities: capabilities
});Regional endpoints:
- US:
ondemand.us-west-1.saucelabs.com - EU:
ondemand.eu-central-1.saucelabs.com - US East:
ondemand.us-east-4.saucelabs.com
Choose the closest region to your CI infrastructure for lower latency.
Step 4: Write a Test
const { remote } = require('webdriverio');
describe('Mobile app tests', () => {
let driver;
before(async () => {
driver = await remote({
protocol: 'https',
hostname: 'ondemand.us-west-1.saucelabs.com',
port: 443,
path: '/wd/hub',
capabilities: {
platformName: 'Android',
'appium:app': 'storage:filename=app-debug.apk',
'appium:deviceName': 'Samsung Galaxy S24',
'appium:platformVersion': '14',
'appium:automationName': 'UiAutomator2',
'sauce:options': {
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY,
name: 'Login test'
}
}
});
});
after(async () => {
// Sauce Labs reads test status from the driver session
if (driver) {
await driver.executeScript('sauce:job-result=passed');
await driver.deleteSession();
}
});
it('completes login successfully', async () => {
const email = await driver.$('~email-input');
await email.setValue('user@example.com');
const password = await driver.$('~password-input');
await password.setValue('SecurePass123');
const submit = await driver.$('~login-button');
await submit.click();
const dashboard = await driver.$('~dashboard-screen');
await dashboard.waitForDisplayed({ timeout: 15000 });
expect(await dashboard.isDisplayed()).toBe(true);
});
});Important: Call sauce:job-result=passed before ending the session. Sauce Labs uses this to mark the test pass/fail in the dashboard.
Reporting Test Status
Set test status explicitly so the Sauce Labs dashboard reflects real results:
// Mark pass
await driver.executeScript('sauce:job-result=passed');
// Mark fail
await driver.executeScript('sauce:job-result=failed');
// In a framework with beforeEach/afterEach:
afterEach(async function() {
if (driver) {
const passed = this.currentTest.state === 'passed';
await driver.executeScript(`sauce:job-result=${passed ? 'passed' : 'failed'}`);
}
});saucectl: Sauce Labs' Test Runner
Sauce Labs provides saucectl — a CLI tool for running tests without writing Appium boilerplate directly.
Install:
npm install -g saucectl
saucectl configure # Enter credentialsConfigure .sauce/config.yml:
apiVersion: v1alpha
kind: xcuitest
sauce:
region: us-west-1
concurrency: 5
xcuitest:
app: storage:filename=MyApp.ipa
testApp: storage:filename=MyApp-Runner.ipa
suites:
- name: "iPhone 15 Pro - iOS 17"
testOptions:
class:
- MyAppTests.LoginTests
- MyAppTests.CheckoutTests
devices:
- name: iPhone 15 Pro
platformVersion: "17"Run:
saucectl runsaucectl handles parallelization, result collection, and CI integration.
Parallel Execution
Run across multiple devices simultaneously:
// wdio.conf.js
exports.config = {
user: process.env.SAUCE_USERNAME,
key: process.env.SAUCE_ACCESS_KEY,
hostname: 'ondemand.us-west-1.saucelabs.com',
port: 443,
path: '/wd/hub',
maxInstances: 10, // 10 parallel sessions
capabilities: [
{
platformName: 'Android',
'appium:deviceName': 'Samsung Galaxy S24',
'appium:platformVersion': '14',
'appium:automationName': 'UiAutomator2',
'appium:app': 'storage:filename=app.apk',
'sauce:options': { build: process.env.BUILD_NAME }
},
{
platformName: 'iOS',
'appium:deviceName': 'iPhone 15 Pro',
'appium:platformVersion': '17',
'appium:automationName': 'XCUITest',
'appium:app': 'storage:filename=app.ipa',
'sauce:options': { build: process.env.BUILD_NAME }
},
// Add more devices...
]
};GitHub Actions Integration
# .github/workflows/sauce-labs.yml
name: Sauce Labs Mobile Tests
on:
push:
branches: [main]
jobs:
mobile-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build Android app
run: ./gradlew assembleDebug
- name: Upload app to Sauce Labs
id: upload
run: |
response=$(curl -s -u "${{ secrets.SAUCE_USERNAME }}:${{ secrets.SAUCE_ACCESS_KEY }}" \
-X POST "https://api.us-west-1.saucelabs.com/v1/storage/upload" \
-H "Content-Type: application/octet-stream" \
-H "Content-Disposition: attachment; filename=app-debug.apk" \
--data-binary @app/build/outputs/apk/debug/app-debug.apk)
echo "app_id=$(echo $response | jq -r '.item.id')" >> $GITHUB_OUTPUT
- name: Run Sauce Labs tests
env:
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
BUILD_NUMBER: ${{ github.run_number }}
run: npm run test:mobileDebugging with Sauce Labs
Live session view: During test execution, open the Sauce Labs dashboard to watch your test run in real-time.
Video replay: Every session records a full video. Available in the dashboard after the session completes.
Appium logs: Complete Appium server logs, filterable by command.
Device logs: System logs (logcat for Android, device console for iOS) captured during the session.
Network capture: Enable with 'sauce:options': { capturePerformance: true }.
Sauce Connect: Testing Private Apps
If your app accesses staging environments or internal APIs not accessible from the internet, use Sauce Connect — a secure tunnel.
# Download Sauce Connect
wget https://saucelabs.com/downloads/sauce-connect/5.0/sauce-connect-5.0-linux.tar.gz
<span class="hljs-comment"># Start tunnel
./sc -u <span class="hljs-variable">$SAUCE_USERNAME -k <span class="hljs-variable">$SAUCE_ACCESS_KEY --tunnel-name my-tunnelAdd to capabilities:
'sauce:options': {
tunnelName: 'my-tunnel'
}Sauce Connect proxies device traffic through your local network, enabling devices to reach internal resources.
Best Practices
Use descriptive test names and builds. The Sauce Labs dashboard is easier to use when tests have meaningful names. Include branch name, build number, and feature name.
Always set job-result. Failing tests that are marked as passed in the dashboard erode trust in your testing infrastructure.
Clean up sessions. Always call driver.deleteSession() — orphaned sessions count against your concurrency limit.
Use private devices for flaky scenarios. Shared device pools have device state from previous sessions. Private devices are freshly provisioned for each session.
Set appropriate timeouts. Real device sessions have network latency. Set waitForDisplayed timeouts to 10-15 seconds minimum.
Group related tests in suites. Session startup takes 30-60 seconds. Tests that are too short amortize startup overhead poorly.
Conclusion
Sauce Labs provides a production-grade mobile testing infrastructure with strong Appium integration, good debugging tools, and enterprise features like Sauce Connect for private environments. The saucectl runner simplifies the boilerplate for common frameworks.
The key workflow: upload app → configure capabilities → run parallel suites → review dashboard for failures. Once CI is configured, it runs without maintenance unless the app substantially changes.
For continuous production monitoring between scheduled test runs, HelpMeTest provides 24/7 automated checks that complement your Sauce Labs test coverage.