Turn your manual testers into automation experts Request a DemoStart testRigor Free

React Native Testing

React Native Testing

React Native is an open-source JavaScript-based mobile app framework that allows developers to build natively-rendered mobile applications for iOS and Android using JavaScript and React. It was first released in 2015 by Facebook as an open-source project and is an extension of the React library, originally designed to build web user interfaces. React Native extends platform support for Android, Android TV, iOS, macOS, tvOS, web, Windows, and UWP(Universal Windows Platform).

React and React Native may sound similar, but they serve different purposes. React, or ReactJS, is a JavaScript library to create website frontends. On the other hand, React Native, powered by React, is used for building native mobile apps. React Native has features like simple UI design, cost efficiency, code reusability, and fast app development. Market giants like Facebook, Instagram, Airbnb, Walmart, Bloomberg, Tesla, and more, use React Native to build mobile apps.

Possessing these incredible features and being so popular, testing plays a crucial role in ensuring the quality of React Native apps. Let's explore the required testing types and tools available for conducting them:

Unit Testing

In isolation, they test minor code units, such as individual functions or classes. Unit testing ensures that each specific code unit behaves as expected and produces the correct output for a given input. React Native bundles with the Jest testing framework, which supports unit testing.

Jest

Jest is a popular and widely used testing framework for JavaScript applications, including React and React Native. Facebook developed it and is known for its simplicity, ease of use, and other powerful features. Jest support is built-in React Native apps, open package.json and check the dev-dependency section. If it's not present, install it by the command npm install --save-dev jest.

The test files are saved with the extension .test.js or .spec.js, and Jest will automatically pick up all the files with the above extension.

Inside the test files, write test cases using Jest's testing APIs. For React Native components, @testing-library/react-native library renders and interacts with components during testing. Jest provides a helpful describe function that organizes the tests by grouping the tests related to a specific functionality. You can even nest the describe functions if needed. Additionally, use beforeEach or beforeAll functions to set up the objects for testing.

Sometimes, when there are dependencies on external services or APIs, it's better to use mocking. It involves replacing the actual external dependency with its implementation for testing purposes. Mock implementations effectively replace the complex and potentially unreliable external code with a simplified version that behaves predictably during testing. And it helps to isolate the specific functionality that needs to be tested and avoid unnecessary dependencies, making the tests faster, more stable, and more reliable.

See an example unit test case for a button component. The filename is ButtonComponent.test.js:
// ButtonComponent.test.js

import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import ButtonComponent from './ButtonComponent';

describe('ButtonComponent', () => {
  test('displays the correct title', () => {
    const { getByText } = render();
    const buttonElement = getByText('Press Me');
    expect(buttonElement).toBeTruthy();
  });
  test('calls the onPress callback when pressed', () => {
    const onPressMock = jest.fn();
    const { getByText } = render();
    const buttonElement = getByText('Press Me');
    fireEvent.press(buttonElement);
    expect(onPressMock).toHaveBeenCalled();
  });
});

Here, describe function is used to group the test cases related to the ButtonComponent. The first test checks if the correct title is displayed on the button, while the second test checks if the onPress callback is called when the button is pressed.

Run the test using the npm test command, and see the results in the terminal or on the command prompt. See the tutorials section of React Native's official website for more details.

Integration Testing

In Integration testing, the different units of the application are combined and tested. It aims to identify issues by combining various units of the Native React app into a more extensive system. These units can be components of the application, external APIs, databases, or any external service.

Integration testing requires testing of the below units for Native React:

Component Integration: Tests how application components render, receive, and pass props, handle state changes, and respond to user interactions. React-native-testing-library is the default library for component integration testing in React Native. Read more in detail about this library in the Component Tests section.

API Integration: Tests API endpoints, request, and response handling, error scenarios, and data synchronization.

Database Integration: Tests data is stored and retrieved accurately in the local database, and any interactions with the database work as intended.

Navigation Integration: Tests that React Native app's navigation and routing work correctly, allowing users to move between screens and navigate seamlessly.

External Services Integration: To test that the app's integration with external services such as social media platforms or payment gateways is working as expected.

Component Testing

In React Native, components play a significant role in rendering the app. Even if the business logic is correct, an error in a component can cause failure. Hence, they are tested to verify that each component renders, responds to user interactions, and handles props and states correctly.

Component testing may also involve testing how components interact with each other when integrated into screens or views. Since components are integral to React Native, they are also tested during unit and integration testing. You can perform component testing in two ways:

  • Testing User Interactions
  • Testing Rendered Output

Testing User Interactions

User interaction testing involves simulating various actions and events that users perform while interacting with the app and verifying that the app responds as expected. Usually, it tests the interactions like tap, scroll, input, swipe, navigation, etc.

While testing the user interactions, the components are tested from a user perspective, meaning the assertions are made based on the UI elements, not the component's properties or state. React Native provides an inbuilt testing library, React Native Testing Library, for testing user interactions.

The following example uses fireEvent methods changeText and press that simulate a user interacting with the component and a query function getAllByText that finds matching Text nodes in the rendered output.
test('given empty GroceryShoppingList, user can add an item to it', () => {
  const {getByPlaceholderText, getByText, getAllByText} = render(
    <GroceryShoppingList />,
  );
  fireEvent.changeText(
    getByPlaceholderText('Enter grocery item'),
      'banana',
  );
  fireEvent.press(getByText('Add the item to list'));
  const bananaElements = getAllByText('banana');
  expect(bananaElements).toHaveLength(1); // expect 'banana' to be on the list
});

Testing Rendered Output

For testing the rendered output, usually, snapshot testing is suggested. A snapshot test case renders a UI component, takes a snapshot, then compares it to a reference snapshot file stored alongside the test.

React Native uses Jest for testing rendered output. Instead of rendering the graphical user interface (UI), Jest uses its custom React serializer to generate a JSX-like string representation to compare during the test.

See sample output below:
<Text
style={
  Object {
    "fontSize": 20,
    "textAlign": "center",
  }
}>
Welcome to React Native!
</Text>

Create and test the component using Jest to test the rendered output. Jest captures a snapshot of the output and saves it as a reference file in the repository. In the future, if any changes to the component affect its output, the test compares the new output to the stored snapshot. If the output is as expected, the snapshot needs to be updated.

However, Snapshot has got many drawbacks:

  • When capturing the reference snapshot, it's essential to ensure no bugs; otherwise, the snapshot will be incorrect and lead to false test results.
  • Also, it will be difficult for a developer or reviewer to understand if the change in the snapshot is intended or is a bug.

End-to-end Testing

They test the entire application, including all its components, services, and external dependencies, to verify that it functions correctly and meets its intended requirements. The primary objective of E2E testing is to simulate real user scenarios and interactions with the application to ensure that it works as expected in a production-like environment. It involves testing the entire mobile application, user interface(UI), user interactions, data flow, navigation, and any backend or API interactions.

You can perform E2E testing of React Native applications with various tools:

  • Detox
  • Appium
  • Maestro
  • testRigor

Detox

Detox is a robust and widely used end-to-end (E2E) testing framework tailored for React Native applications. It supports both real devices and emulators/simulators, providing flexibility for testing across various platforms. With its intuitive API, Detox allows developers to write test scripts that simulate user actions like tapping, swiping, and entering text, verifying the application's functionality, navigation, and user experience. Detox supports JavaScript programming.

See a sample test case created with Detox. It covers logging in to the Amazon app, adding an item to the cart, and completing the checkout process.
describe('Amazon Shopping Flow', () => {
  beforeAll(async () => {
    await device.launchApp();
  });

  it('should log in to Amazon', async () => {
    await element(by.id('loginEmailInput')).typeText('testuser@example.com');
    await element(by.id('loginPasswordInput')).typeText('password123');
    await element(by.id('loginButton')).tap();
  });
  
	it('should search for Men\'s Printed T-Shirt', async () => {
    await element(by.id('searchInput')).typeText('Men\'s Printed T-Shirt');
    await element(by.id('searchButton')).tap();
  });
  
	it('should select a Men\'s Printed T-Shirt', async () => {
    await element(by.id('productItem_0')).tap();
  });
  
	it('should add the selected T-Shirt to the cart', async () => {
    await element(by.id('addToCartButton')).tap();
  });
  
	it('should proceed to checkout', async () => {
    await element(by.id('checkoutButton')).tap();
  });
  
	it('should complete the checkout process', async () => {
    await element(by.id('shippingAddressInput')).typeText('123 Main Street');
    await element(by.id('paymentInput')).typeText('Credit Card Number');
    await element(by.id('placeOrderButton')).tap();
  });
});

Detox comes with valuable features like cross-platform support, easy debugging, compatibility with real devices, and automatic synchronization of tests.

Appium

Appium is a popular open-source testing framework for mobile applications that supports React Native apps. It supports scripting in different languages and execution in real devices and emulators. Using selectors like IDs or accessibility labels, Appium identifies and simulates user interactions, such as taps and swipes, to validate the app's behavior and responsiveness.

However, it doesn't have inbuilt integrations with other tools like CI/CD tools, test management tools, or any cloud device labs. So mostly, it's preferred to combine with any testing frameworks like Selenium or WebdriverIO. Configuring the device for Appium can be challenging, and running test scripts simultaneously in parallel is not straightforward. This difficulty is because Appium relies on a specific port-hosted server for executing the tests. The code style for Appium is primarily similar to Detox.

Maestro

Maestro is an open-source mobile testing framework compatible with Android and iOS. It's straightforward and intuitive, and you can create test scenarios using YAML. The main advantages of using Maestro are fast test execution, significantly less flaky tests, simple setup, and support for different platforms.

See a sample Maestro YAML file below:
# flow_contacts_android.yaml
appId: com.android.contacts
---
- launchApp
- tapOn: "Create new contact"
- tapOn: "First Name"
- inputText: "John"
- tapOn: "Last Name"
- inputText: "Snow"
- tapOn: "Save"

However, Maestro has got many limitations:

  • No support for detailed reports
  • Real iOS devices are not supported yet
  • Not able to do parameterization
  • Won't support parallel execution
  • Not possible to write complicated tests

Challenges in Automating React Native Tests

Dynamic UI Elements: React Native applications frequently feature dynamic user interfaces (UI), with components rendered conditionally or based on data. Automated testing of these dynamic elements using traditional test automation tools can present challenges. Identifying and effectively interacting with such dynamic UI elements may require specialized automation approaches and modern testing tools.

Unstable Tests: React Native applications interact with external services, APIs, and device features. Flaky test results may arise from network delays, slow API responses, or timing issues, causing inconsistencies in test outcomes.

Explicit Waits: React Native heavily relies on asynchronous operations. Handling asynchronous actions through legacy automation tests can be complex and may require explicit wait conditions to ensure that tests proceed in sync with the application.

React Native Updates: The frequent updates and changes in React Native can impact the stability and compatibility of automation testing frameworks and tools. As a result, tests may require updates and maintenance to align with the latest versions of React Native for seamless functioning, or the tests will break.

Cross-Platform Tests: React Native enables app development for both Android and iOS platforms. Ensuring cross-platform compatibility in automation tests demands meticulous handling of test cases and configurations tailored to each platform.

Test Dependencies: React Native apps often use third-party libraries and dependencies. Managing and handling these dependencies in automation tests can be complex and require additional configurations such as emulators or simulators. These devices might not fully replicate real-device behavior and can lead to discrepancies between test results and actual device performance.

How testRigor Solves these Challenges?

Testing is critical in ensuring app quality before release, and it should be both speedy and thorough. However, the E2E testing tools we previously discussed have certain drawbacks that hinder these objectives. Many of these tools lack essential features for mobile app testing.

For instance, cross-platform testing or complex test execution is crucial for acceptable test coverage.

The issues mentioned earlier are why organizations are seeking better alternatives to speed up the test automation process and enhance coverage to keep pace with Agile/DevOps methodologies. Organizations cannot afford to spend excessive time on script maintenance due to challenges like dynamic element locators, frequent React Native updates, and asynchronous operations, among others.

To address these concerns, many companies turn to testRigor. TestRigor is an innovative no-code automation tool infused with integrated AI, specifically designed to accelerate test creation and minimize test maintenance.

Below are some advantages that set testRigor apart from other tools:

Versatile test creation: You can create tests through three easy approaches:

  • Provide only the test case title and testRigor's generative AI engine automatically creates test steps for you within seconds.
  • testRigor helps to write the test scripts in plain English by eliminating the prerequisite to know any programming language. testRigor's AI converts the English test scripts to actual code using advanced Natural Language Processing (NLP).
  • Use our test recorder to record your actions and create the test case easily. Tests are generated in plain English, so there is no code to learn. The absence of XPath dependency ensures ultra-stable tests that are easy to maintain.

Self-healing tests: The issue of constant updates and test maintenance is expected in React Native since it is regularly updated with new versions. testRigor incredibly manages these issues with its automatic self-healing features. Any changes in the element attributes or application are incorporated into the test scripts automatically, saving massive maintenance effort hours.

Automatic wait times: testRigor automatically manages wait times. It spares you from manually inputting explicit waits to manage asynchronous operations programmed in React Native. This automatic wait prevents "element not found" errors which are ubiquitous sight while using legacy automation testing tools.

Ultra-stable: As a no-code solution, testRigor eliminates dependencies on specific programming languages. Elements are referenced as they appear on the screen, reducing reliance on implementation details and simplifying test creation, maintenance, and debugging.

Cloud-hosted: Save time, effort, and resources on infrastructure setup. With testRigor, you are ready to start writing test scripts right after signing up, boasting a speed up to 15 times faster compared to other automation tools.

Comprehensive testing coverage: testRigor supports all main types of testing and accommodates cross-browser and cross-platform tests for web, mobile, iOS, Android, Desktop, and almost everything!

Seamless integrations: Built-in integrations with CI/CD, ERP systems such as SalesForce, NetSuite, WorkDay, SAP ERP, ServiceNow, and test management tools ensure a seamless and efficient testing process.

Take a look at the testRigor's documentation to get a better understanding of the supported functionality.

See a testRigor's simple automated test case below:
login
enter "Men's Printed Shirt" roughly to the left of  "search"
click "Search"
click on image from stored value "Men's Printed Shirt" with less than "10" % discrepancy
click "Add to Cart"
click "Cart"
click "Checkout"
check that page contains "Enter your zip code"

You can see how straightforward and clean the test script appears. The in-built login command manages the login process seamlessly. Simply write in plain English, using what you see on the screen as element locators. There's no need for CSS and XPath element locators, which often lead to flaky and unstable tests. testRigor meets all your React Native testing requirements with simplicity, allowing anyone on the team to write and execute test cases in plain English.

These features enable you to achieve comprehensive test coverage well before the release deadline and within your budget. You don't need separate testing tools or external integrations for React Native testing. The integrated reporting, logs, error messages, and video recordings of your test runs allow for better and more informed decision-making.

Explore testRigor's remarkable features.

Conclusion

Testing is crucial to React Native app development to ensure it functions as expected and upholds quality. Any lag in the speed or functionality of the mobile app can prompt users to switch to a competitor's app within seconds. While React Native empowers developers to create efficient cross-platform mobile apps, thorough testing is paramount to identify and address issues before release.

We've observed that many existing testing tools present limitations, hindering the full potential of React Native's robust features. Opting for a comprehensive test automation tool guarantees swift delivery of a top-notch React Native app. These measures ensure the app meets user expectations, delivers consistent performance across various platforms and devices, and retains its user base.

Join the next wave of functional testing now.
A testRigor specialist will walk you through our platform with a custom demo.