Testing Your React Native App With Expo & Appium
— Testing, JavaScript, Automation, Software Development — 5 min read
At the start of July I decided to bite the bullet and take a run at building my own app, JiffyCV. I had built a number of React Native apps for others but this was going to be all mine and so far it’s been an incredibly rewarding experience.
I started the project by doing some user research and then used that to build a series of mockups in Figma to carry out usability testing on the solution I felt delivered the most value to the user.
After I was happy that the app was usable and valuable I started turning those designs into React components using Storybook and Expo. This allowed me to quickly turn around the components, but there was one aspect missing.
I wanted to be able to test the components on multiple levels during development. Unit tests can only do so much and can’t tell you if everything works on different devices, so I started looking at UI automation.
I initially started looking at Detox as it allows you to get closer to the React Native runtime and this means you can check more of the inner workings of the app as opposed to a fully black-box solution.
Unfortunately Detox does not play well with Expo and even when I built a brand new React Native app from scratch I could never really get it to work so I retired that idea relatively quickly.
The next tool I looked at was Appium which doesn’t state that it works with Expo. There are ways of making it play nice enough with the Expo Client to allow you to run your automation without the need to build the standalone binary.
I’ll be covering how to run Appium against both the Expo Client and a standalone app as the only difference is the capabilities and the way to setup the client before running your tests.
Setting Up Appium
Compared to Detox, setting up Appium can feel a little daunting but the appium-doctor
tool is a massive time saver as it’ll tell you if your environment is configured correctly.
I’m not going to cover how to install all the dependencies as that would be an article itself, but there are a number of blog posts kicking around the internet that help with this.
The main things you’ll want to focus on are getting Appium Desktop and either the Android emulator or iOS simulator running (I found the Android setup easier on both Linux and Mac so I’d advise starting there).
Once you have Appium Desktop working with the emulator you can use the emulator to run the Expo Client & load your app at which point you can use Appium Desktop’s Session interface (use the capabilities below) to connect to the emulator and start seeing the structure of your app’s UI.
1{2 "platformName": "android",3 "deviceName": "[NAME OF YOUR EMULATOR]"4}
Automating the Expo Client with Appium
There’s not much information on the internet about using Appium with the Expo Client but after you learn the ‘secret trick’ it’s actually pretty easy to get it working on different platforms.
When you launch the Expo Client you may notice that the Expo apps have a exp://
link and this link is what we’ll use to launch the app under test in the Expo Client.
There’s no easy cross-platform way to do this though as iOS requires a more convoluted approach than Android but if you abstract the logic out into functions you can build re-usable commands to handle all this.
The execution flow for launching the app in the Expo Client on Android looks like this:
- Launch the Expo Client app
- Close the Expo Client app
- Launch the Expo Client app again
- Execute a
mobile:deeplink
toexp://127.0.0.1:19000
(or applicable hostname) - Wait for an element on the first page of the app to be visible
Technically steps 2 and 3 could be replaced with sending the keycode 82 as that should trigger a Expo Client reload which is needed to ensure that the state of the app is reset.
For iOS there’s a bit more complexity:
- Launch Safari
- Enter
exp://127.0.0.1:19000
(or applicable hostname) into the Safari address bar - Accept the loading of the Expo Client (through automation or using the
autoAcceptAlerts
capability) - Deal with the first run pop up if needed (press the ‘Got It’ button followed by the close button for the dev menu)
- Reload the Expo Client by sending a
shake()
command and pressing the ‘Reload’ button in the dev menu - Wait for an element on the first page of the app to be visible
iOS is made even more awkward thanks to how Apple seem to change the behaviour of the Safari URL input between versions of iOS, for instance on iOS 13.3 the URL bar is a button that needs to be pressed before you can input anything but on iOS 13.6 it’s an auto-focused text field.
Once you’ve got the app under test loaded in the Expo Client and the Expo Client reloaded so the app state is reset then you can start the actual automation of your app.
Here’s the capabilities and functions I’m using for Android
And the capabilities and functions I’m using for iOS
Automating a standalone binary generated by Expo
Luckily a standalone binary is a lot easier to get up and running as you can just use the capabilities to install the app and run it or have it boot an existing installation.
I’ve found the latter to be more reliable, at least on Android where I use adb install [PATH TO APK]
to install the .apk on the emulator before running Appium with the following capabilities.
Automating the Storybook React Native UI
As I mentioned at the start of the article my use case for Expo at the moment is to build my component library up for the app I’ll be eventually building.
The are a few reasons I chose to automate the component library and not just use the automation in the main app.
- I’m building the automation using the Atomic Design pattern so it makes sense to have my ‘atom’, ‘molecule’ and ‘organism’ page objects in the same code base as the ‘atom’, ‘molecule’ and ‘organism’ level components that make up my app’s UI
- If I can catch automation issues in the component library I can prevent these from making it into the main app and fix them earlier
- The component library is used to show off the different states that components can have so it’s an ideal candidate for building the page objects against
- This leaves the main app’s code base to be concerned only with laying out screen & handling the app’s state and likewise, the automation at that level will model the ‘template’ and ‘page’ level page objects.
There’s only a couple actions that are needed to automate the storybook UI:
- We need to be able to select a story to load from the story list
- We need to be able to load the preview for the selected story
- We need to be able to search for a story, especially if the story list gets too long
Below is a snippet of a simple atomic structure for this:
Next Steps: Automating Android and iOS apps with Appium
Over the following weeks I’ll be posting about Appium and how I’m using it as well as any gotchas, architectural patterns and how to get it running in CI.
If you’re working with Appium or looking to start using it feel free to leave a comment below with any questions you may have and I’ll try to answer. Give me a follow if you want to read my next posts in this series.