How to Apply UI Test Automation in React Native Apps?
React Native has changed the way we think about the mobile apps development process but didn’t change the main goal of software development teams—delivering the best quality products as soon as possible. However, it definitely comes with its own set of challenges every developer needs to conquer. I will do my best to outline the most import ones, along with some tips on how to solve them. I have also prepared a short guide to Detox (in my opinion the best hybrid testing framework) and how to set it up for Continuous Integration systems.
Challenges for React Native UI testing
More complex setup
npm / yarn dependencies and resolve conflicts in platform-specific libraries.
View elements recognition
For example, let’s consider a text view from a sample app defined like:
As on the screenshots below, for iOS
testID is interpreted as an accessibility identifier but for Android, it’s missing. Hopefully,
accessibilityLabel and the text are interpreted almost the same for both platforms.
The first thing you will surely come up against is dealing with app permissions. Make sure that each test can be launched separately and that a permission pop up will not ruin the execution. For native scripts, there are a lot of plugins and ways to sort out this problem. Some hybrid frameworks can also take care of this (warning: Cavy doesn’t!) so don’t be afraid to check them out.
Some app states and local databases are sometimes shared between separated tests. This problem really affects everyone who works on UI testing automation. In order to reduce the risk of shared app states between tests, you can use platform specific approaches—e.g Android Test Orchestrator (described in more detail in one of our blog posts) or implement custom methods that clear cache and memory files or remove any kind of data. React Native dedicated frameworks that I’ve checked (Cavy, Detox) also cover that, together with implemented cleanup methods.
Native approaches give more white-box solutions for common problems. What’s also really helpful, is that the open source community gives more tools and frameworks that make testing easier, e.g. mocking tools, simulator hacks. Native frameworks make deeper integration with the app possible. By adding extra identifiers to view elements, you can make all of them as accessible as the native apps.
Judging by popularity, speed and entry level, I recommend using Espresso (for a single-package testing) and UIAutomator (for a multiple-package testing). As I mentioned before, React Native doesn’t support setting up
R.xml file). It’s a problem as matching by
resource-id is a standard practice when using Espresso and using it with React Native means limiting yourself to using only a text and
There are few ways of doing native iOS automation. You can use XCUITest if you are looking for a low-cost setup solution (provided directly from Xcode), but depending on your needs, you can also use more powerful, mature frameworks that are based on XCUITest.
Google also remembers about iOS testers and released the EarlGrey framework—a tool similar to Espresso but running on iOS. Fast, reliable and powerful tool deeply integrated within the app source code that allows many users actions.
There are ways to avoid writing a code for each platform that we develop—just like we do by using React Native.
I did a research on React Native dedicated tools and it turns out there are plenty of frameworks that allow for writing one code for both platforms:
I’ve decided not to write more about Appium because of its very poor performance and complex setup. Another framework I considered was Cavy. All Cavy matchers are based on custom testHook identifiers (cannot refer to text values), which makes Cavy very fast but they need to be put in each view element. What’s more, Cavy affects the index file and it’s a big change when it comes to apps. Finally, I discovered Detox—automation framework for React Native and Native applications. Detox combines EarlGrey and Espresso—native automation stack for iOS and Android. Native stack makes Detox very fast, powerful and easy for CI integration.
I’ve prepared a simple guide on how to use Detox with CI server (Gitlab CI).
Automation guide with Detox
I’ve created a very primitive app, which transforms a button into the Polidea logo. It was simple, but enough for this guide’s purpose.
I’ve created a sample test class where I’ve defined 4 test methods which use Detox API and access views by text and ID. Notice how easily you can check the appearing views.
I’ve created a configuration for running Detox both on Android and iOS as below:
React Native app with tests configured as above can be built by using these commands:
detox build -c ios.sim.debug & detox build -c android.emu.debug
For running those tests on Android and iOS just execute:
detox test -c ios.sim.debug & detox test -c android.emu.debug
As you can see, running Detox tests is very simple and can be easily moved to the CI system. Let’s take a look at a plan for CI integration—all we need is to include the following steps inside our test job:
- Install missing project dependencies and Detox
- Launch React Native packager
- Build an app using Detox
- Run Detox tests
- Terminate the packager
You can check it on my simplified (no caching, no separate steps) iOS and Android snippet for Gitlab CI setup:
As the result for
detox_test:iOS you’ll get the output:
In my article, I’ve presented approaches for React Native UI test automation and explained how easily it can be achieved with a Detox framework. The solution I’ve provided is dedicated for Gitlab CI that is used by Polidea for Continuous Integration, but you can easily adapt those steps to your needs. You can find my sample project on Polidea github.