AWS Builder Center
Test Custom Mobile Apps with Amazon Nova Act

Test Custom Mobile Apps with Amazon Nova Act

Upload your custom app to Device Farm and test login flows, alerts, and assertions with natural language — replacing 200+ lines of Appium code with 80 lines of English.

Co-authored with Shyam Soundar 

Introduction

In Part 1 , you learned to automate pre-installed apps on real devices with plain English commands. This post takes the next step: you upload your own APK, test login flows, handle alerts, and verify device state — all without writing a single XPath selector.
You will build a 4-test suite in 80 lines of Python that replaces 7 files and 200+ lines of traditional Appium code.

What You Will Build

A test suite for the AWS Device Farm Android Reference App covering:
  1. Valid login — enter credentials, verify success message
  2. Invalid login — verify error handling
  3. Alert dialogs — trigger, verify content, dismiss
  4. Fixture checks — confirm Bluetooth, GPS, NFC, and WiFi status

Setup

1
2
3
4
5
6
git clone https://github.com/srcsakthivel/getting-started-device-farm-with-nova-act.git
cd getting-started-device-farm-with-nova-act

pip install -r requirements.txt
cp .env.example .env
# Edit .env with your workflow definition name and device ARN
The repository bundles the APK at apps/AWSDeviceFarmAndroidReferenceApp.apk. Clone and run — no extra downloads.

Handling Credentials Securely

The problem

Embedding credentials in act() prompts triggers Nova Act's safety guardrails:
1
2
3
# ❌ Triggers guardrails — credential strings in AI prompt
nova.act("Enter 'admin' in the username field")
nova.act("Enter 'wrongpassword' in the password field")
The AI model interprets these as real credentials and blocks the action.

The fix: type_text()

Send keystrokes at the OS level. The value never reaches the AI prompt:
1
2
3
4
5
# ✅ Credentials bypass the AI prompt entirely
def enter_text_in_field(nova, field_label: str, value: str) -> None:
nova.act(f"Tap on the '{field_label}' input field to focus it.")
nova.clear_focused() # Clear existing text first
nova.type_text(value) # Sends keystrokes via Appium, not AI

Five patterns for reliable tests

PatternReason
type_text() for all inputsKeeps values out of the AI prompt
clear_focused() before typingPrevents concatenation with stale text
One action per act() callCompound prompts reduce reliability
Separate actions from verificationsTap first, then verify in a second call
Match exact UI labelsUse "Login Page" not "login"

The Complete Test Suite

File: src/android_custom_app.py

Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os
from dotenv import load_dotenv
from nova_act import workflow

load_dotenv(os.path.join(os.path.dirname(os.path.dirname(
os.path.abspath(__file__))), ".env"))

WORKFLOW_DEFINITION_NAME = os.environ["NOVA_ACT_WORKFLOW_DEFINITION_NAME"]
APP_PATH = os.path.join(os.path.dirname(os.path.dirname(
os.path.abspath(__file__))), "apps", "AWSDeviceFarmAndroidReferenceApp.apk")

LOGIN_USERNAME = os.environ.get("LOGIN_USERNAME", "admin")
LOGIN_PASSWORD = os.environ.get("LOGIN_PASSWORD", "")
DEFAULT_DEVICE_ARN = os.environ["DEVICE_FARM_DEVICE_ARN"]

Reusable helpers

1
2
3
4
5
6
7
8
def navigate_to_page(nova, page_name: str) -> None:
nova.act("Tap the hamburger menu icon to open the navigation drawer.")
nova.act(f"Tap on '{page_name}' in the navigation drawer menu.")

def enter_text_in_field(nova, field_label: str, value: str) -> None:
nova.act(f"Tap on the '{field_label}' input field to focus it.")
nova.clear_focused()
nova.type_text(value)

Test 1: Valid login

1
2
3
4
5
navigate_to_page(nova, "Login Page")
enter_text_in_field(nova, "Username Input Field", LOGIN_USERNAME)
enter_text_in_field(nova, "Password Input Field", LOGIN_PASSWORD)
nova.act("Tap the 'Login' button.")
nova.act("Verify the text 'You are logged on as admin' is visible.")

Test 2: Invalid login

1
2
3
4
5
navigate_to_page(nova, "Login Page")
enter_text_in_field(nova, "Username Input Field", LOGIN_USERNAME)
enter_text_in_field(nova, "Password Input Field", INVALID_PASSWORD)
nova.act("Tap the 'Login' button.")
nova.act("Verify the text 'You gave me the wrong username and password' is visible.")

Test 3: Alert dialog

1
2
3
4
navigate_to_page(nova, "Alerts")
nova.act("Tap the button that triggers an alert notification.")
nova.act("Verify an alert shows the message 'This is the alert message'.")
nova.act("Tap 'OK' to dismiss the alert.")

Test 4: Device fixtures

1
2
3
4
5
navigate_to_page(nova, "Fixtures")
nova.act("Verify Bluetooth status shows 'true' or 'false'.")
nova.act("Verify GPS status shows 'true' or 'false'.")
nova.act("Verify NFC status shows 'true' or 'false'.")
nova.act("Verify WiFi status shows 'true' or 'false'.")

Run the suite

1
2
cd src/
python android_custom_app.py
Output:
1
2
3
4
5
6
7
🚀 Starting Nova Act Test Suite — Device Farm Reference App
✅ App uploaded and installed on Device Farm — Nova Act ready!
📋 TEST 1: Login with valid credentials ✅ PASSED
📋 TEST 2: Login with invalid credentials ✅ PASSED
📋 TEST 3: Alert dialog ✅ PASSED
📋 TEST 4: Fixtures (device toggles) ✅ PASSED
🎉 ALL TESTS PASSED!

How App Upload Works

When you pass app_path, NovaActMobile handles the full lifecycle:
  1. Uploads the APK to Device Farm (skips re-upload for identical files)
  2. Installs the app on the device
  3. Launches the specified activity
  4. Tears down the session on exit
1
2
3
4
5
6
7
with NovaActMobile(
app_package=APP_PACKAGE,
app_activity=APP_ACTIVITY,
app_path=APP_PATH, # ← triggers upload
device_arn=DEFAULT_DEVICE_ARN,
) as nova:
# App is installed and running — start testing

Traditional Appium vs Nova Act

Traditional AppiumNova Act
Files7 (6 Page Objects + 1 spec)1
Code200+ lines~80 lines
SelectorsResource IDs, UiSelectorsPlain English
MaintenanceBreaks on UI changesResilient
NavigationScroll + wait loopsnavigate_to_page(nova, "Login Page")
See src/android_traditional_appium.py for the full side-by-side comparison.

Test Your Own App

Use src/custom_app_getting_started.py as a template:
1
2
3
4
python custom_app_getting_started.py \
--app-path ../apps/your-app.apk \
--app-package com.yourcompany.app \
--app-activity .MainActivity

Cost

ComponentPrice
AWS Device Farm$0.17/device minute (1,000 min free with Free Tier)
Amazon Nova Act$4.75 per agent hour (pricing )
This 4-test suite~$1.25 (5 min device time + ~5 min agent time)

Conclusion

You built a full mobile test suite in 80 lines of Python. The five patterns — type_text(), clear_focused(), one-action-per-call, separate verifications, and exact UI labels — produce reliable tests that survive UI redesigns. Clone the repo, set your .env, and run your first test in under 5 minutes.

Resources


Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article