Assessing the Top Tools and Best Practices for Testing Cross-Platform React Native Applications
By Manos Konstantinidis and Sherrylene Gauci
We take a deep dive into testing tools and best practices that will help you test React Native mobile applications
Mobile applications have become an essential part of our lives. As app demand continues to grow, developers are faced with the challenge of building apps that work across multiple platforms. React Native (RN) is a popular framework created by Meta Platforms Inc. which enables developers to build cross-platform apps using JavaScript or TypeScript.
In this blog post, we will take a deep dive into a few recommended testing tools and best practices that can help you test React Native applications across different layers of the testing pyramid.
Unit tests
One of the most common types of tests is unit tests. These are often written by the developers who are implementing a feature and serve the purpose of testing individual components of an app in isolation.
As the scope of the functionality that is being tested is limited, unit tests have quite a few advantages as they are easy to write and maintain, don’t take a significant amount of time to run and are very consistent — especially compared to other test suites that are testing multiple parts of an application.
There is also React Native Testing Library, which has become the go-to tool for testing React Native components. It enables rendering components on top of react-test-renderer and provides helpers for rendering and interacting with the UI.
Jest and React Native testing library
When developing cross-platform mobile applications with React Native, the tooling around unit tests is similar to the tools used for web applications made with React. This means getting up to speed is very straightforward.
Jest is one of the most popular testing libraries and it has been around since 2012. Its API is broadly used and similar to other well-known Javascript testing libraries like Mocha, Sinon and Chai.
End-to-end tests
End-to-end (E2E) tests are a widely debate topic in the software development world. Most engineers acknowledge the value that these bring from an end-user perspective. But why are they called E2E tests and how do they fit into the world of mobile applications? We’ll explain.
E2E tests traditionally sit at the very top of the testing pyramid — yes, they are located far away from the application code for a reason. The higher up the pyramid, the closer the tests are to real-world situations. When users engage with an app, they are using the app with a use case in mind and E2E tests have to replicate similar behaviour. Of course, this presents some challenges because apps can behave in unexpected ways, especially with so many devices available on the market.
Within the React Native space, there are a few tools built to help with E2E testing, which we will look into in more detail below.
Webdriver.io
//webdriver.io - example test
describe('Login', () => {
beforeEach(async () => {
await TabBar.waitForTabBarShown();
await TabBar.openLogin();
await LoginScreen.waitForIsShown(true);
});
it('should be able login successfully', async () => {
await LoginScreen.tapOnLoginContainerButton();
await LoginScreen.submitLoginForm({ username: 'john', password: '1234' });
await NativeAlert.waitForIsShown();
await expect(await NativeAlert.text()).toEqual('You are logged in!');
await NativeAlert.topOnButtonWithText('OK');
await NativeAlert.waitForIsShown(false);
});
});
Webdriver.io is one of the older tools on the block. Repurposed to work with JavaScript and TypeScript (JS/TS) based mobile applications using the Appium API, it offers a fresh approach to building accessible and simple E2E tests.
On top of that, Webdriver.io also integrates with well-known commercial cloud platforms such as BrowserStack, further expanding cross-platform testing. What’s not to like?
It supports behaviour-driven languages such as Cucumber, integrates with most CI/CD platforms and is widely supported by the test community. Being around for so long surely makes Cucumber more mature, reliable and stable. It does, however, face some competition with newer, open-sourced test frameworks such as Detox and Maestro.
Maestro
# flow_ios.yaml
appId: com.nearform.demoapp
---
- launchApp
- assertVisible: 'Creating Digital Advantage'
- tapOn:
id: 'loginButton'
- assertVisible: 'Hello World!'
Maestro is the newest library for E2E tests and is already quite popular. Built on learnings from other similar tools, Maestro works with both native (iOS & Android) and React Native apps. It has some simplistic but powerful tools that make writing, running and debugging tests very easy.
Maestro requires minimal setup, and you can write Flows (think of a Flow as a user ‘journey’) using Yaml, making Flows readable to everyone.
Maestro Studio is an app that is built-in into the Maestro CLI. It allows you to interact with elements of your app, discover ways to write assertions and, in general, make the experience of running tests quick, easy and fun.
Detox
// e2e/first-test.js
describe('Example', () => {
beforeAll(async () => {
await device.launchApp();
});
it('should tap on the button and expect visible text', async () => {
await element(by.id('ButtonID')).tap();
await expect(element(by.text('Success!'))).toBeVisible();
});
});
Built by the engineering team at Wix, Detox is a powerful library for writing E2E tests for cross-platform mobile applications written with React Native. After the initial setup, writing test cases is pretty straightforward, since it uses Jest as the default test runner which is familiar to most software engineers.
Detox runs smoothly on all CI environments. However, you will need a macOS agent to run tests on iOS simulators. It can also run on real Android devices, making it possible to run Detox tests on BrowserStack — iOS is not supported at the time of writing.
Test frameworks comparison
Visual testing
Visual regression testing is the process of verifying visual differences in the UI to help detect visual bugs. Visual testing complements E2E testing — in fact, several visual tools work well with most commercial E2E frameworks. Visual testing tools have evolved greatly in recent years.
Percy.io
Percy.io started off as an open source tool focused on web applications. However, it has come a long way since then. It is now owned by BrowserStack, a cloud platform that supports testing across multiple devices and platforms. More recently, Percy.io launched its beta product for native apps, making it more versatile and very easy to set up visual regression tests for mobile projects.
The Percy visual engine is capable of detecting pixel-level changes and highlighting them via its diff highlighter functionality. It also uses anti-aliasing algorithms to recognise noise in screenshot comparisons, like page and text shifting, as well as false positives when comparing diffs.
Visual testing tools like Percy can present some challenges when dealing with dynamic images and text, which can cause flakiness in test results. For mobile apps, screens can shift around during loading or rendering when the user is swiping or scrolling.
However, Percy provides solutions to these issues, such as adjusting the difficulty sensitivity settings or using mock or fake test data libraries. These solutions can help ensure more reliable and accurate test results.
Visual testing works well with continuous integration and delivery (CI/CD) tooling and aims to improve the quality of the code and the code-releasing process.
Continuous integration
Continuous integration is a key aspect of running tests for regression purposes. One best practice is to organise the pipeline stages in the correct order. For example, running unit test jobs should precede E2E and visual testing jobs to enable an early feedback loop, since they are faster to run and closer to the app code.
It is also good practice to run E2E tests in parallel in the pipeline to make them more efficient and speedy.
Automated triggers can be set up to initiate scheduled test runs at specific times of the day (for example a nightly schedule) as these won’t require manual intervention.
Finally, ensuring visibility and timely notifications to the relevant teams about test failures and issues can be done through platforms like Slack or Teams. This helps to deliver quick problem resolutions.
Conclusion
From our experiences, we’ve found that including the above-mentioned testing layers as part of the software engineering process is highly beneficial. However, when it comes to prioritising which type of tests to use for your project, it can vary depending on several factors — such as requirements, the size of the team and the delivery goals.
We’ve learned that automating the testing process is absolutely crucial and we feel that it’s very important to establish a robust and optimised testing process.
Putting in the effort to fine-tune the testing approach makes all the difference and we believe it will do the same for you when you’re building top-notch mobile applications.