Next-Gen App & Browser Testing Cloud
Trusted by 2 Mn+ QAs & Devs to accelerate their release cycles

Learn about the React Native framework & how to perform React Native testing on Android & iOS apps using manual and automation testing approaches.

Faisal Khatri
May 4, 2026
React Native powers high-traffic apps including Facebook, Instagram, Shopify, and Microsoft Office. Shipping a single JavaScript codebase to both Android and iOS speeds up development, but it also means a regression on either platform reaches every user.
This tutorial walks through end-to-end React Native testing on real Android and iOS devices using Appium 2.x with TestNG and Java, then runs the same suite in parallel on the TestMu AI real device cloud. By the end, you have a working Page Object Model project, a testng.xml that runs both platforms in parallel, and dashboard build artifacts you can show your team.
Key Takeaways
React Native is an open-source JavaScript framework from Meta for building cross-platform mobile apps. It ships a single JavaScript codebase to iOS and Android, rendering native views (UIView, ViewGroup) instead of a WebView, with an escape hatch into Swift, Objective-C, Java, or Kotlin when needed.
With 126k stars on the React Native GitHub repository, it is one of the most active open-source projects in the mobile ecosystem. That reach is also why testing matters: a single regression in shared JavaScript code hits every Android and iOS user at once.
React Native testing maps onto the standard testing pyramid, but the cross-platform nature adds one extra concern: every layer needs to validate both Android and iOS behavior, not just JavaScript correctness.
For a deeper look at the component layer, see our guide to React testing libraries. The rest of this article focuses on the E2E layer, where the differences between Android and iOS bite hardest.
Most React Native teams ship a mix of these tools rather than picking one. The decision is which tool owns which layer.
| Tool | Test Layer | Runs On | Best For |
|---|---|---|---|
| Jest | Unit | Node (no device) | Pure functions, reducers, hooks logic. Default with React Native. |
| React Native Testing Library | Component | Node (no device) | Asserting UI output and user interactions at the component level. |
| Detox | E2E (gray-box) | Simulator / emulator | React Native specific E2E with synchronization built in. JavaScript only. |
| Appium | E2E (black-box) | Real Android and iOS devices | Cross-platform real-device testing with Java, Python, JavaScript, or C# bindings. Works on any app, not just React Native. |
When to choose Appium for React Native:
If your needs are JavaScript-only and you do not require physical devices, start with Jest plus React Native Testing Library and reach for Detox or Appium when those layers stop catching the bugs you care about. The rest of this guide builds out the Appium-on-real-devices flow.
To follow along, install Java 11 or later, Appium Server 2.5+ (Appium 3.x is also supported and uses the same WebDriver protocol), Appium Java Client 9.x, TestNG as the runner, and Maven as the build tool. The cloud target is the TestMu AI mobile grid, which removes the need to maintain a local device farm.
npm install -g appium.appium driver install uiautomator2 for Android and appium driver install xcuitest for iOS.pom.xml (covered in the implementation section below).appium server
Note: The legacy Appium Desktop GUI is deprecated. Use the Appium server CLI plus a separate Appium Inspector install instead.
Note: Run React Native tests on 10,000+ real Android and iOS devices in parallel. Try TestMu AI free
This section walks through a working Appium + TestNG project that runs the same React Native app against real Android and iOS devices on the TestMu AI cloud. The full source is on GitHub; this section explains the moving parts.
The demo apps are the Proverbial sample app (a React Native build that exercises text fields, notifications, toasts, geolocation, and an in-app browser):
TestMu AI is an AI-native test execution platform that runs manual and automated tests across 10,000+ real Android and iOS devices. The Appium grid sits behind the same WebDriver hub, so the only client-side change is the desired-capabilities block. See the real-device app testing docs for a full setup walkthrough.
The suite covers seven user flows that touch React Native's most common cross-platform pain points: native rendering, OS-level notifications, toast messages, geolocation, navigation, and an embedded WebView.
The project uses Maven for builds and TestNG for the runner. Add the Appium Java Client, TestNG, and Lombok to pom.xml:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.testng/testng -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng-version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.Appium/java-client -->
<dependency>
<groupId>io.Appium</groupId>
<artifactId>java-client</artifactId>
<version>${Appium-java-client-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok-version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
Versions of the dependencies are set in a separate properties block. It is done for maintainability, so if we need to update the versions, we can do it easily without searching throughout the pom.xml file.
<properties>
<testng-version>9.2.2</testng-version>
<Appium-java-client-version>7.9.0</Appium-java-client-version>
<lombok-version>1.18.32</lombok-version>
</properties>
In the Page Object Model (POM), a class is created for every web page, and it contains all the related elements (such as buttons, text fields, etc.) of that page, as well as the relevant action methods (such as clicking a button, entering text, etc.). This separation of concerns helps keep the page objects separate from the test code, improving maintainability and readability.
We will be create four different classes for all four different pages, namely, HomePage, GeoLocationPage, SpeedTestPage, and BrowserPage where specific locators of their respective pages will be stored, and we would be calling these locators in the tests to check the application.
Below, we will create Android and iOS classes for each action point mentioned in the test scenario above. First, let’s locate the elements for the required components on Android and iOS, enabling us to execute the tests.
HomePage class for Android
public class HomePage {
AndroidDriverManager driverManager;
WebDriverWait wait;
public HomePage (final AndroidDriverManager driverManager) {
this.driverManager = driverManager;
wait = new WebDriverWait (driverManager.getDriver (), Duration.ofSeconds (20));
}
public WebElement textBtn () {
return driverManager.getDriver ()
.findElement (AppiumBy.id ("Text"));
}
public String getText () {
return driverManager.getDriver ()
.findElement (AppiumBy.id ("Textbox"))
.getText ();
}
public WebElement notificationBtn () {
return driverManager.getDriver ()
.findElement (AppiumBy.id ("notification"));
}
public WebElement notificationBar () {
return driverManager.getDriver ()
.findElement (AppiumBy.id ("action_bar"));
}
public WebElement toastBtn () {
return driverManager.getDriver ()
.findElement (AppiumBy.id ("toast"));
}
public String toastMessage () {
return wait.until (ExpectedConditions.presenceOfElementLocated (AppiumBy.xpath ("//android.widget.Toast[1]")))
.getText ();
}
public WebElement geoLocationBtn () {
return driverManager.getDriver ()
.findElement (AppiumBy.id ("geoLocation"));
}
public WebElement speedTestBtn () {
return driverManager.getDriver ()
.findElement (AppiumBy.id ("speedTest"));
}
public WebElement browserMenu () {
return driverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("Browser"));
}
public void openMenu () {
driverManager.getDriver ().findElement (AppiumBy.accessibilityId ("drawer open")).click ();
}
public void clickPushNotificationMenu () {
driverManager.getDriver ().findElement (AppiumBy.id ("pushNotification")).click ();
}
}
HomePage class for iOS
public class HomePage {
private final IOSDriverManager iosDriverManager;
private final WebDriverWait wait;
public HomePage (final IOSDriverManager iosDriverManager) {
this.iosDriverManager = iosDriverManager;
this.wait = new WebDriverWait (iosDriverManager.getDriver (), Duration.ofSeconds (20));
}
public WebElement textBtn () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("Text"));
}
public String getText () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("Textbox"))
.getText ();
}
public WebElement notificationBtn () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("notification"));
}
public WebElement notificationBar () {
return this.wait.until (
ExpectedConditions.presenceOfElementLocated (AppiumBy.accessibilityId ("NotificationShortLookView")));
}
public WebElement toastBtn () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("toast"));
}
public String toastMessage () {
return this.wait.until (ExpectedConditions.presenceOfElementLocated (
AppiumBy.xpath ("//*[contains(@label, 'Toast should be visible')]")))
.getText ();
}
public WebElement geoLocationBtn () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("geoLocation"));
}
public WebElement speedTestBtn () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("speedTest"));
}
public WebElement browserMenu () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("Browser"));
}
}
From the code, it is clear that all the elements on the HomePage on Android were located using the ID locator strategy in most cases. In contrast, accessibilityId was used for the browser menu. On iOS, accessibilityId was used to locate all the elements.
Here, we will identify the Android and iOS mobile applications WebElements on the GeoLocationPage.
GeoLocationPage class for Android
public class GeoLocationPage {
private final AndroidDriverManager driverManager;
private final WebDriverWait wait;
public GeoLocationPage (final AndroidDriverManager driverManager) {
this.driverManager = driverManager;
this.wait = new WebDriverWait (driverManager.getDriver (), Duration.ofSeconds (30));
}
public WebElement content () {
return this.wait.until (ExpectedConditions.presenceOfElementLocated (AppiumBy.id ("android:id/content")));
}
public void navigateToHomePage () {
this.driverManager.getDriver ()
.navigate ()
.back ();
}
}
GeoLocationPage class for iOS
public class GeoLocationPage {
private final IOSDriverManager iosDriverManager;
private final WebDriverWait wait;
public GeoLocationPage (final IOSDriverManager iosDriverManager) {
this.iosDriverManager = iosDriverManager;
this.wait = new WebDriverWait (iosDriverManager.getDriver (), Duration.ofSeconds (30));
}
public WebElement banner () {
return this.wait.until (ExpectedConditions.presenceOfElementLocated (AppiumBy.accessibilityId ("banner")));
}
public WebElement backBtn () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("Back"));
}
public void navigateToHomePage () {
clickOn (backBtn ());
}
}
Similar to the approach on the HomePage, a locator for checking the content was found using the Id locator strategy for Android. For iOS, accessibilityId was used to locate the banner.
It’s important to note the navigateToHomePage() method, which was created to take the user back to the HomePage once the required actions are completed for the tests on Android. For iOS, a back button is available in the app, so it was located using accessibilityId, and a click was performed to navigate to the HomePage.
Here, we will identify the WebElements on the SpeedTestPage for Android and iOS.
SpeedTestPage class for Android
public class SpeedTestPage {
private final AndroidDriverManager driverManager;
public SpeedTestPage (final AndroidDriverManager driverManager) {
this.driverManager = driverManager;
}
public WebElement headerText () {
return this.driverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("Speedtest"));
}
public void navigateToHomePage () {
this.driverManager.getDriver ()
.navigate ()
.back ();
}
}
SpeedTestPage class for iOS
public class SpeedTestPage {
private final IOSDriverManager iosDriverManager;
public SpeedTestPage (final IOSDriverManager iosDriverManager) {
this.iosDriverManager = iosDriverManager;
}
public String headerText () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.iOSClassChain ("**/XCUIElementTypeImage[`label == "Speedtest"`]"))
.getText ();
}
public void navigateToHomePage () {
this.iosDriverManager.getDriver ()
.navigate ()
.back ();
}
}
The code for the SpeedTestPage for Android is self-explanatory. The iOSClassChainlocator strategy was used to locate the Speedtest label for locating elements on iOS.
Here, we will identify the WebElements on the BrowserPage for Android and iOS mobile applications.
BrowserPage class for Android
public class BrowserPage {
private AndroidDriverManager driverManager;
public BrowserPage (final AndroidDriverManager driverManager) {
this.driverManager = driverManager;
}
public WebElement searchBox () {
return driverManager.getDriver ()
.findElement (AppiumBy.id ("url"));
}
public void searchFor (String url) {
searchBox ().sendKeys (url);
clickOn (findBtn ());
waitForsomeTime ();
}
public WebElement findBtn () {
return driverManager.getDriver ()
.findElement (AppiumBy.id ("find"));
}
public void navigateToHomePage () {
driverManager.getDriver ()
.navigate ()
.back ();
}
}
The WebElements for searchBox() and findBtn() on the BrowsePage on the Android app were located using the id locator. The searchFor() method is created to search for and navigate to the URL.
BrowserPage class for iOS
public class BrowserPage {
private final IOSDriverManager iosDriverManager;
public BrowserPage (final IOSDriverManager iosDriverManager) {
this.iosDriverManager = iosDriverManager;
}
public WebElement searchBox () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("url"));
}
public void searchFor (String url) {
searchBox ().sendKeys (url);
clickOn (findBtn ());
waitForsomeTime ();
}
public WebElement findBtn () {
return this.iosDriverManager.getDriver ()
.findElement (AppiumBy.accessibilityId ("Find"));
}
public void navigateToHomePage () {
this.iosDriverManager.getDriver ()
.navigate ()
.back ();
}
}
As we did for the Android app, the same thing is repeated in the iOS app class; the searchBox() and findBtn() buttons are located using the accessibilityId locator. The searchFor() method is created to navigate to a URL from the BrowserPage.
Let's define the BaseTest classes that can be extended in the actual tests to avoid repeating the same configuration. Two separate BaseTest classes are created to instantiate the Android and iOS drivers.
public class BaseTest {
protected AndroidDriverManager androidDriverManager;
@Parameters({"buildName", "testName", "app", "platformName", "platformVersion", "deviceName"})
@BeforeClass
public void setupTest(final String buildName, final String testName, @Optional("app") final String app, final String platformName, final String platformVersion,
final String deviceName) {
this.androidDriverManager = AndroidDriverManager.builder()
.buildName(buildName)
.testName(testName)
.app(app)
.platformName(platformName)
.platformVersion(platformVersion)
.deviceName(deviceName)
.build()
.createAndroidDriver();
}
@AfterClass
public void tearDown() {
this.androidDriverManager.quitDriver();
}
}
public class BaseTest {
protected IOSDriverManager iosDriverManager;
@Parameters({"buildName", "testName", "app", "platformName", "platformVersion", "deviceName"})
@BeforeClass
public void setupTest(final String buildName, final String testName, @Optional("app") final String app, final String platformName, final String platformVersion,
final String deviceName) {
this.iosDriverManager = IOSDriverManager.builder()
.buildName(buildName)
.testName(testName)
.app(app)
.platformName(platformName)
.platformVersion(platformVersion)
.deviceName(deviceName)
.build()
.createIOSDriver();
}
@AfterClass
public void tearDown() {
this.iosDriverManager.quitDriver();
}
}
As you can see, buildName, testName, app, platformName, version, and device are all captured as a part of @Parameters TestNG annotation, which will be set using the testng.xml file.
Next, the builder design pattern is used to build the instance for driverManager and pass the respective values accordingly so we can run tests on the desired configurations.
Let’s dive deep into the test automation strategy we defined earlier and start writing the tests.
Two packages were created for Android and iOS tests. Similarly, two packages are created for Android and iOS pages, as locators differ for both mobile applications.
With the page object classes and their respective locators set, we can now use those page classes to write tests and verify the scenarios discussed earlier as part of the test strategy.
In addition to automation testing on real devices, developers also use React Testing Libraries for unit and component-level testing to validate UI rendering before integrating mobile automation tests.
Android Tests
public class AndroidTests extends BaseTest {
private HomePage homePage;
private BrowserPage browserPage;
private GeoLocationPage geoLocationPage;
private SpeedTestPage speedTestPage;
@BeforeClass
public void setupTest() {
this.homePage = new HomePage(this.androidDriverManager);
this.browserPage = new BrowserPage(this.androidDriverManager);
this.geoLocationPage = new GeoLocationPage(this.androidDriverManager);
this.speedTestPage = new SpeedTestPage(this.androidDriverManager);
}
}
iOS Tests
public class IOSTests extends BaseTest {
private HomePage homePage;
private GeoLocationPage geoLocationPage;
private BrowserPage browserPage;
private SpeedTestPage speedTestPage;
@BeforeClass
public void setupTest () {
this.homePage = new HomePage (this.iosDriverManager);
this.geoLocationPage = new GeoLocationPage (this.iosDriverManager);
this.browserPage = new BrowserPage (this.iosDriverManager);
this.speedTestPage = new SpeedTestPage (this.iosDriverManager);
}
}
Code Walkthrough
Open the App and check the welcome message – "Hello! Welcome to lambdatest Sample App called Proverbial" is displayed correctly. Tap the TEXT button and check that "Proverbial" is displayed.
Now tap the NOTIFICATION button to check that the notification is on top. Also, check that the notification appears correctly and tap to close it. Tap the TOAST button to check that the toast message is displayed at the bottom of the screen and verify its text “Toast should be visible”.
After that, tap the GEOLOCATION button to check that the app navigates successfully to the geolocation page. Once navigation to the geolocation page is successful, navigate back to the home page.
Then, tap the SPEED TEST button to verify the displayed banner by verifying the app navigates to the speed test page. After verification, navigate back to the home page.
The assertTrue() in Java is used in the Android tests to check if the SPEEDTEST header is displayed. It is because no locator was found that returned the text to be asserted. On the other hand, in iOS, a locator was available to locate the header text; hence, the assertEquals() method is used to verify the header text.
Tap the Browser menu at the bottom of the screen. Once the app navigates to the browser page, enter the text "TestMu AI" and click the Find button to check if the website loads.
In this, both Android and iOS tests use the clickOn() function to tap on the browser menu element on the home page. Additionally, they use the searchFor() function on the browser page object to search for https://lambdatest.com.
Test Execution
The first and most important step is to set up the drivers so they can be used to run the test. Here, two separate classes, AndroidDriverManager and IOSDriverManager, have been created to manage the Android and iOS drivers, respectively.
Username, Access Key, and Grid URL are defined as constant(static final) values as these will not change anytime throughout the tests. You can get your Username and Access Key from your TestMu AI Account Settings > Password & Security.
The desired capabilities are dynamic values set in the getLambdaTestOptions() method, which changes according to Android and iOS automation testing requirements. With the breaking changes in Appium 2.0, the UiAutomator2Options class is used for Android capabilities, and the XCUITestOptions class is used for iOS capabilities.
You can generate the desired capabilities from the TestMu AI Automation Capabilities Generator.
To generate an app_url for your test script, go to the TestMu AI App Automation dashboard and click the Upload App icon.

Upload your .apk or .ipa file to the TestMu AI platform to generate an app_url. The app_url is unique each time you upload. Click Real Device under the App tab and browse to the file.

Once the file is uploaded, you get the unique app_url and capabilities to copy into your test script where you have defined the TestMu AI capabilities.

With the Username, Access Key, and app_url from TestMu AI in hand, paste these details into your test script.
Configuration (FileName – AndroidDriverManager)
@Builder
public class AndroidDriverManager {
private static final ThreadLocal<AndroidDriver> DRIVER = new ThreadLocal<>();
private String buildName;
private String testName;
private String platformName;
private String platformVersion;
private String deviceName;
private String app;
private static final String LT_USERNAME = System.getenv("LT_USERNAME");
private static final String LT_ACCESS_KEY = System.getenv("LT_ACCESS_KEY");
private static final String GRID_URL = "@mobile-hub.lambdatest.com/wd/hub";
@SneakyThrows
public AndroidDriverManager createAndroidDriver() {
DRIVER.set(new AndroidDriver(new URL(format("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_KEY, GRID_URL)),
uiAutomator2Options()));
setupDriverTimeouts();
return this;
}
public AndroidDriver getDriver() {
return DRIVER.get();
}
public void quitDriver() {
if (null != DRIVER.get()) {
getDriver().quit();
DRIVER.remove();
}
}
private void setupDriverTimeouts() {
getDriver().manage()
.timeouts()
.implicitlyWait(Duration.ofSeconds(30));
}
private UiAutomator2Options uiAutomator2Options() {
final UiAutomator2Options uiAutomator2Options = new UiAutomator2Options();
uiAutomator2Options
.setAutoGrantPermissions(true)
.withBrowserName("Chrome")
.setCapability("lt:Options", getLambdaTestOptions());
return uiAutomator2Options;
}
private HashMap<String, Object> getLambdaTestOptions() {
final HashMap<String, Object> ltOptions = new HashMap<>();
ltOptions.put("w3c", true);
ltOptions.put("platformName", this.platformName);
ltOptions.put("platformVersion", this.platformVersion);
ltOptions.put("deviceName", this.deviceName);
ltOptions.put("app", this.app);
ltOptions.put("build", this.buildName);
ltOptions.put("name", this.testName);
ltOptions.put("autoGrantPermissions", true);
ltOptions.put("isRealMobile", true);
ltOptions.put("visual", true);
ltOptions.put("console", true);
ltOptions.put("devicelog", true);
ltOptions.put("plugin", "java-testNG");
return ltOptions;
}
Configuration (FileName – IOSDriverManager)
@Builder
public class IOSDriverManager {
private static final ThreadLocal<IOSDriver> DRIVER = new ThreadLocal<>();
private String buildName;
private String testName;
private String platformName;
private String platformVersion;
private String deviceName;
private String app;
private static final String LT_USERNAME = System.getenv("LT_USERNAME");
private static final String LT_ACCESS_KEY = System.getenv("LT_ACCESS_KEY");
private static final String GRID_URL = "@mobile-hub.lambdatest.com/wd/hub";
@SneakyThrows
public IOSDriverManager createIOSDriver() {
DRIVER.set(new IOSDriver(new URL(format("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_KEY, GRID_URL)),
xcuiTestOptions()));
setupDriverTimeouts();
return this;
}
private XCUITestOptions xcuiTestOptions() {
final XCUITestOptions xcuiTestOptions = new XCUITestOptions();
xcuiTestOptions
.setAutoAcceptAlerts(true)
.setAutoDismissAlerts(true)
.withBrowserName("chrome")
.setCapability("lt:Options", getLambdaTestOptions());
return xcuiTestOptions;
}
public IOSDriver getDriver() {
return DRIVER.get();
}
public void quitDriver() {
if (null != DRIVER.get()) {
getDriver().quit();
DRIVER.remove();
}
}
private void setupDriverTimeouts() {
getDriver().manage()
.timeouts()
.implicitlyWait(Duration.ofSeconds(30));
}
private HashMap<String, Object> getLambdaTestOptions() {
final HashMap<String, Object> ltOptions = new HashMap<>();
ltOptions.put("w3c", true);
ltOptions.put("platformName", this.platformName);
ltOptions.put("platformVersion", this.platformVersion);
ltOptions.put("deviceName", this.deviceName);
ltOptions.put("app", this.app);
ltOptions.put("build", this.buildName);
ltOptions.put("name", this.testName);
ltOptions.put("autoAcceptAlerts", true);
ltOptions.put("autoDismissAlerts", true);
ltOptions.put("isRealMobile", true);
ltOptions.put("visual", true);
ltOptions.put("console", true);
ltOptions.put("devicelog", true);
ltOptions.put("plugin", "java-testNG");
return ltOptions;
}
}

The ThreadLocal class sets the drivers in the above code because it is thread-safe and works well when tests run in parallel. Using ThreadLocal ensures that two threads cannot see each other’s ThreadLocal variables, even if they set different values on the same ThreadLocal object.
An important note is that Lombok’s @Builder annotation is used in this class. This annotation allows us to build and obtain the desired capabilities details at runtime without passing the respective parameters in the method signature.
Note: Appium Server does not need to run locally when tests target the TestMu AI cloud grid.
Configuration parameters like platform name, platform version, device name, app, build name, and test name are set using the testng.xml file. These tests run on real Android and iOS devices on the TestMu AI platform.
The TestNG suite enables parallel execution by setting parallel="tests" and thread-count="2", which runs Android and iOS suites simultaneously rather than sequentially. For larger matrices, scale this with the Appium parallel testing pattern.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="LambdaTest Mobile automation test suite" parallel="tests" thread-count="2" >
<test name="Proverbial app - Android Mobile Automation">
<parameter name="buildName" value="Android Build"/>
<parameter name="testName" value="Proverbial app tests"/>
<parameter name="app" value="lt://APP1016026231710500086926825"/>
<parameter name="platformName" value="ANDROID"/>
<parameter name="platformVersion" value="13"/>
<parameter name="deviceName" value="Galaxy S23"/>
<classes>
<class name="io.github.mfaisalkhatri.mobileautomation.tests.android.AndroidTests">
<methods>
<include name="textTests"/>
<include name="notificationTest"/>
<include name="toastMessageTest"/>
<include name="geoLocationTest"/>
<include name="speedTestPageTest"/>
<include name="browserTest"/>
</methods>
</class>
</classes>
</test> <!-- Test -->
<test name="Proverbial app - iOS Mobile Automation">
<parameter name="buildName" value="IOS Build"/>
<parameter name="testName" value="Proverbial app tests"/>
<parameter name="app" value="lt://APP1016026231710499882750475"/>
<parameter name="platformName" value="IOS"/>
<parameter name="platformVersion" value="16"/>
<parameter name="deviceName" value="iPhone 14 Pro"/>
<classes>
<class name="io.github.mfaisalkhatri.mobileautomation.tests.ios.IOSTests">
<methods>
<include name="textTests"/>
<include name="notificationTest"/>
<include name="toastMessageTest"/>
<include name="geoLocationTest"/>
<include name="speedTestPageTest"/>
<include name="browserTest"/>
</methods>
</class>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Trigger the following command on the terminal to run the tests using Maven:
mvn clean install -DLT_USERNAME=<LambdaTest username> -DLT_ACCESS_KEY=<LambdaTest Access Key>
Once the tests run, the TestMu AI App Automation dashboard surfaces video recordings, screenshots, console logs, and device logs for each session, scoped per build.
Android build dashboard:

iOS build dashboard:

Start by cloning the react-native-app-tests repo, plug your TestMu AI LT_USERNAME and LT_ACCESS_KEY into the environment, upload the Proverbial .apk and .ipa to App Automation, and run mvn clean install. The same Page Object Model and TestNG suite scales to your real React Native app by swapping the locators in HomePage and BrowserPage.
For broader cross-platform context, see how the same patterns apply in React Native best practices, or compare cross-platform frameworks in our Flutter vs React Native guide. To explore the cloud platform, visit the mobile app testing page or read the real-device app testing docs.
Note: This article was researched and drafted with AI assistance, then reviewed, fact-checked, and published by Faisal Khatri, Community Contributor at TestMu AI, whose listed expertise includes App Testing and WebdriverIO Appium. Every statistic, link, and product claim was verified against primary sources. Read our editorial process and AI use policy for details.
Did you find this page helpful?
More Related Hubs
TestMu AI forEnterprise
Get access to solutions built on Enterprise
grade security, privacy, & compliance