UI testing your React Native apps using Maestro
Table of contents

Maestro simplifies mobile user interface (UI) testing with its YAML-based approach that works seamlessly with React Native applications. This article covers everything from installation and setup, to writing test flows and implementing advanced testing strategies for production-ready React Native apps.
Testing mobile applications is difficult, and even more so when using cross-platform frameworks like React Native. Traditional testing frameworks often require complex setup, extensive configuration, and preexisting knowledge of platform-specific testing tools. Enter Maestro — an easy-to-use mobile UI testing framework.
Whether you’re building a simple productivity app or a complex document management system like those powered by Nutrient React Native SDK, reliable UI testing ensures your users have a consistent, bug-free experience across iOS and Android platforms.
At Nutrient, we use Maestro to test our React Native SDK with end-to-end tests, ensuring document viewing, annotation, form filling, and other features work consistently across platforms.
Integrate document workflows and native functionality into your React Native app with ease.
Why UI testing matters for React Native apps
UI testing goes beyond unit tests by validating how users actually interact with your application. For React Native apps, this means ensuring:
- Cross-platform consistency — Your app behaves the same on iOS and Android
- User workflows function correctly — Complex interactions like form submissions, annotations, navigation flows, and data persistence work as expected
The limitations of traditional React Native testing
Traditional React Native testing tools like Jest and React Native Testing Library can only test the JavaScript layer of your application; they cannot execute or validate native code functionality. This creates significant testing gaps when using React Native libraries that are built on native SDKs such as Nutrient React Native SDK. For apps using React Native SDKs that bridge to native functionality, end-to-end testing with tools like Maestro becomes essential to validate the complete user experience and flows, including the native code execution that traditional testing cannot reach.
What is Maestro?
Maestro(opens in a new tab) is a mobile UI testing framework built on learnings from existing frameworks like Appium, Espresso, UIAutomator, and XCTest. Unlike traditional testing frameworks that require complex programming, Maestro uses simple YAML files to define test flows.
Key advantages of Maestro
Simplicity first — Tests are written in YAML, making them readable and maintainable by both developers and QA teams.
Cross-platform support — One framework works for iOS, Android, React Native, and Flutter.
No complex setup — Maestro requires minimal configuration to get started.
CI/CD friendly — Lightweight architecture integrates easily with continuous integration pipelines, and it also supports cloud-based testing.
Prerequisites
Before getting started with Maestro, ensure you have the following:
- A React Native development environment set up with the React Native CLI(opens in a new tab)
- iOS Simulator (for iOS testing); Android Emulator or an Android device (for Android testing)
- macOS or Linux (Maestro doesn’t support Windows natively)
- Node.js 16+ for your React Native project
- Basic YAML knowledge (though Maestro’s syntax is straightforward)
Step 1 — Installing Maestro
Install Maestro using the official installer script:
curl -Ls "https://get.maestro.mobile.dev" | bash
Verify the installation:
maestro -v
For iOS testing, you’ll also need Facebook IDB:
pip3 install fb-idb
Step 2 — Setting up your React Native project for testing
Next, you’ll create a sample React Native app to demonstrate Maestro testing. If you already have a project, you can skip to the configuration steps.
Creating a test-ready React Native app
npx react-native@latest init MaestroTestAppcd MaestroTestApp
Adding testID properties
Maestro interacts with components using either text content or testID
properties. Add testID
to components you want to test:
import React, { useState } from "react";import { SafeAreaView, Text, TextInput, TouchableOpacity, Alert,} from "react-native";
const App = () => { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [isLoggedIn, setIsLoggedIn] = useState(false);
const handleLogin = () => { if (username && password) { setIsLoggedIn(true); Alert.alert("Success", "Login successful!"); } else { Alert.alert("Error", "Please fill in all fields"); } };
const handleLogout = () => { setIsLoggedIn(false); setUsername(""); setPassword(""); };
if (isLoggedIn) { return ( <SafeAreaView style={{ flex: 1, padding: 20, justifyContent: "center" }}> <Text testID="welcome-message">Welcome, {username}!</Text> <TouchableOpacity testID="logout-button" onPress={handleLogout}> <Text>Logout</Text> </TouchableOpacity> </SafeAreaView> ); }
return ( <SafeAreaView style={{ flex: 1, padding: 20, justifyContent: "center" }}> <Text testID="login-title">Log in to your account</Text>
<TextInput testID="username-input" placeholder="Username" value={username} onChangeText={setUsername} autoCapitalize="none" />
<TextInput testID="password-input" placeholder="Password" value={password} onChangeText={setPassword} secureTextEntry />
<TouchableOpacity testID="login-button" onPress={handleLogin}> <Text>Login</Text> </TouchableOpacity> </SafeAreaView> );};
export default App;
Step 3 — Creating your first Maestro test flow
Create a .maestro
directory in your project root and add your first test file:
mkdir .maestro
Basic login flow test
Create .maestro/login-flow.yaml
:
touch .maestro/login-flow.yaml
appId: com.maestrotestapp---- launchApp- assertVisible: "Log in to your account"
# Test successful login- tapOn: id: "username-input"- inputText: "testuser"- tapOn: id: "password-input"- inputText: "password123"- tapOn: id: "login-button"
# Verify login success- assertVisible: "Welcome, testuser!"- assertVisible: id: "logout-button"
# Test logout- tapOn: id: "logout-button"
# Verify return to login screen- assertVisible: "Log in to your account"- assertVisible: id: "username-input"
Step 4 — Running your tests
Before executing any tests, the React Native app must be built and installed on the target device or simulator. This ensures Maestro can interact with a fully compiled version of the application during testing.
Build and install your app
First, build your React Native app:
# For iOSnpx react-native run-ios
# For Androidnpx react-native run-android
Execute Maestro tests
Run a single test flow:
maestro test .maestro/login-flow.yaml
Run all tests in the directory:
maestro test .maestro/
Understanding test output
Maestro provides detailed output for each test step:
⚠️ Flow login-flow.yaml✅ Launch app "com.maestrotestapp"✅ Assert that "Log in to your account" is visible✅ Tap on id: "username-input"✅ Input text "testuser"✅ Tap on id: "password-input"✅ Input text "password123"✅ Tap on id: "login-button"✅ Assert that "Welcome, testuser!" is visible✅ Assert that logout-button is visible✅ Tap on id: "logout-button"✅ Assert that "Log in to your account" is visible✅ Assert that username-input is visible
Flow succeeded
Advanced Maestro techniques for React Native
With basic tests in place, advanced techniques like scrolling, reusable variables, and AI-powered validations enable more robust and flexible test flows.
Working with ScrollViews and navigation
# Test scrolling and navigation- scroll- scrollUntilVisible: element: id: "bottom-button" direction: DOWN
Using variables for reusable tests
# Test with different dataenv: USERNAME: "testuser" PASSWORD: "password123"---- tapOn: id: "username-input"- inputText: ${USERNAME}- tapOn: id: "password-input"- inputText: ${PASSWORD}
AI-powered testing with Maestro
Maestro also includes AI capabilities that can enhance your testing workflows.
AI-powered assertions
Use AI to validate complex UI states and content:
# AI-powered visual validation.- assertWithAI: "Verify the login form is properly displayed"- assertNoDefectsWithAi: "Check for any visual defects on the screen"
# AI-powered text extraction.- extractTextWithAI: "Extract the error message text"
AI configuration
To use AI commands like assertWithAI
and assertNoDefectsWithAi
, you need to configure an API key. Maestro uses custom AI models through its cloud backend, requiring the MAESTRO_CLOUD_API_KEY
environment variable:
export MAESTRO_CLOUD_API_KEY=your_api_key_here
Account required: You need a Maestro account to use AI features. Create a free account at maestro.dev(opens in a new tab). Some features might require a paid subscription.
AI testing is particularly valuable for React Native apps where UI components may render differently across platforms, or when dealing with dynamic content from APIs.
AI assistant integration with MCP
Maestro supports the Model Context Protocol (MCP), which allows AI assistants like Claude, Cursor, or Windsurf to interact directly with your Maestro tests. This enables AI-powered test generation, debugging, and analysis:
# MCP is included with Maestro CLI.maestro mcp
Configure your MCP client (like Claude Desktop) to connect to Maestro:
{ "mcpServers": { "maestro": { "command": "maestro", "args": ["mcp"] } }}
With MCP integration, AI assistants can:
- Generate test flows based on your app’s UI
- Debug failing tests with intelligent analysis
- Suggest improvements to existing test suites
- Analyze test coverage and recommend additional scenarios
Using Maestro Studio for visual test creation
Maestro Studio is a visual tool that simplifies test creation:
maestro studio
This opens a GUI where you can:
- Record interactions by clicking your app
- Generate YAML automatically from your actions
- Inspect element properties to find the best selectors
- Debug failing tests with visual step-by-step execution
Maestro Studio is particularly useful when working with complex UI components or when you need to identify hard-to-select elements, either by ID or screen coordinates.
Integrating Maestro with CI/CD
Maestro integrates seamlessly with CI/CD pipelines. Simply install Maestro in your CI environment and run your tests:
# Install Maestro.curl -Ls "https://get.maestro.mobile.dev" | bash
# Run tests.maestro test .maestro/
For cloud-based testing, Maestro offers cloud execution, which handles device provisioning and parallel test execution automatically.
Conclusion
Maestro’s YAML-based approach makes it easy to create and maintain tests, giving you confidence in changes and additions made to your React Native apps.
By following the practices outlined in this guide, you can:
- Reduce testing overhead with simple, maintainable test flows
- Increase confidence in your React Native apps across platforms
- Catch issues early with automated UI testing in your CI/CD pipeline
With Maestro, testing is no longer an afterthought — it becomes a reliable part of your development process, helping you ship better apps faster and with greater confidence.
Combine the power of Maestro with Nutrient React Native SDK to build, test, and ship reliable document workflows across iOS and Android.