mut - Mobile UI Testing CLI
Run YAML-based mobile UI tests anywhere - CI/CD, local development, or scripts.
Features
- Run anywhere - No Claude Code dependency, works in CI/CD
- Fast screenshots - ~50ms via scrcpy frame buffer
- AI verification - Gemini 2.5 Flash for visual assertions
- Video recording - Capture test execution for debugging
- Record tests - Capture user interactions with approval UI
- Conditional actions - Branch based on screen state
- Verbose logging - Debug-level file logging for troubleshooting
Installation
pip install mut
Requirements
- Python 3.11+
- Android device/emulator with USB debugging enabled
adbin PATHscrcpy3.x installedGOOGLE_API_KEYenvironment variable (for AI features)
Quick Start
Run a test
mut run tests/login.yaml
Record a test
mut record login-flow --app com.example.app
# Interact with your app...
mut stop
# Opens approval UI in browser
List devices
mut devices
YAML Test Format
config:
app: com.example.app
setup:
- terminate_app
- launch_app
- wait: 2s
tests:
- name: Login flow
steps:
- tap: "Email"
- type: "user@test.com"
- tap: "Password"
- type: "secret123"
- tap: "Sign In"
- verify_screen: "Welcome screen with user greeting"
Actions Reference
Basic Actions
| Action | Description | Example |
|---|---|---|
tap |
Tap element by text or coordinates | tap: "Submit" or tap: [540, 1200] |
type |
Type text into focused field | type: "hello@example.com" |
swipe |
Swipe gesture | swipe: {direction: up, distance: 300} |
long_press |
Long press element | long_press: "Item" |
back |
Press back button | back |
hide_keyboard |
Dismiss keyboard | hide_keyboard |
Wait Actions
| Action | Description | Example |
|---|---|---|
wait |
Wait fixed duration | wait: 2s |
wait_for |
Wait for element to appear | wait_for: "Loading complete" |
scroll_to |
Scroll until element visible | scroll_to: "Terms of Service" |
App Lifecycle
| Action | Description | Example |
|---|---|---|
launch_app |
Launch configured app | launch_app |
terminate_app |
Force stop app | terminate_app |
AI Verification
| Action | Description | Example |
|---|---|---|
verify_screen |
Assert screen matches description (deferred) | verify_screen: "Login form visible" |
Conditional Actions
# Execute steps if element is present
- if_present: "Skip Tutorial"
then:
- tap: "Skip Tutorial"
# Execute steps if element is absent
- if_absent: "Login Button"
then:
- tap: "Sign Up"
# Branch based on screen state (AI-powered)
- if_screen: "2FA prompt shown"
then:
- tap: "Skip for now"
else:
- tap: "Continue"
Commands
mut run <test.yaml>
Execute a YAML test file.
# Basic execution
mut run tests/login.yaml
# Specify device
mut run tests/login.yaml --device emulator-5554
# Custom output directory
mut run tests/login.yaml --output reports/
# Generate JUnit XML report
mut run tests/login.yaml --junit results.xml
mut record <name>
Start recording user interactions.
mut record checkout-flow --app com.example.shop
mut stop
Stop recording and generate YAML test.
mut stop
# Opens browser with approval UI
# Review and edit steps
# Export YAML when done
mut devices
List connected devices.
mut devices
mut report <dir>
Generate HTML report from JSON results.
mut report tests/reports/2026-01-16_login/
Configuration
Environment Variables
| Variable | Description |
|---|---|
GOOGLE_API_KEY |
Gemini API key for AI features |
MUT_DEVICE |
Default device ID |
MUT_VERBOSE |
Enable verbose file logging (true/false) |
Global config (~/.mut.yaml)
ai:
model: gemini-2.5-flash
defaults:
video: true
verbose: false
Project config (.mut.yaml)
device: emulator-5554
test_dir: tests/
report_dir: reports/
verbose: true
Verbose Logging
Enable debug-level file logging for troubleshooting:
# Via .env file (recommended)
echo "MUT_VERBOSE=true" >> .env
# Via environment variable
MUT_VERBOSE=true mut run tests/login.yaml
Log file locations:
- Recording:
tests/<name>/debug.log - Test run:
tests/<name>/runs/<timestamp>/debug.log
Logs include: step execution, element search, AI calls, timing, retries.
Recording Workflow
- Start recording:
mut record my-test --app com.example.app - Interact with app: Touch, type, swipe - all captured with video
- Stop recording:
mut stop - Review in browser: Approval UI opens automatically
- Edit steps: Adjust descriptions, add verifications
- Export YAML: Download the generated test file
Recording folder structure:
tests/my-test/
├── video.mp4 # Screen recording
├── video.timestamps.json # Frame timing data
├── touch_events.json # Raw touch data
├── analysis.json # AI step analysis
└── debug.log # Verbose logs (if enabled)
Test Run Structure
Each test run creates a timestamped folder:
tests/my-test/
├── test.yaml # Test definition
└── runs/
└── 2026-01-17_14-30-25/
└── debug.log # Run-specific logs
CI/CD Integration
GitHub Actions
name: Mobile UI Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install mut
run: pip install mut
- name: Run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
script: mut run tests/login.yaml --junit results.xml
env:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: results.xml
License
MIT
Metadata
Release files for mutcli 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mutcli-0.3.0.tar.gz | 303.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mutcli-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 435.0 kB
Release files / mutcli-0.3.0.tar.gz
| Download URL | mutcli-0.3.0.tar.gz |
|---|---|
| Size | 303.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9e3238dbed366e408073aefc70b6eecf582e424708149b747883a1a9451abf81
|
|
BLAKE2b-256 checksum How to use checksums |
8ebd4802548148a603d62b6b4223e48a8ec6105c98dbccc4a78dcb99c4a47d0b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.14
|
Release files / mutcli-0.3.0-py3-none-any.whl
| Download URL | mutcli-0.3.0-py3-none-any.whl |
|---|---|
| Size | 131.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f11cc84aed6b43d022c903c3e0e26863e7ca322a617ac6d8c1b2965524a2d365
|
|
BLAKE2b-256 checksum How to use checksums |
48e0c6e1cd216379ceec929f02b69c4bfe0fff5e90ccf39cf41ef1e38c2fc861
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.14
|