Automated React Native tests using UI Automator
Writing unit tests for React Native is fairly straight forward using tools like Jest or AVA. However, I had a few questions from our QA department about integration tests, so I decided to play around with Android’s UI Automator a little bit. This is a quick walkthrough of what I explored. The repo I built can be found here.
Create the React Native app
There’s plenty of guides and additional complexities to this, but I just followed the simple instructions under “Building Projects with Native Code” section on React Native getting started. I quickly threw together a TouchableOpacity that toggles a green square on and off.
The plan was to test the presence of the box and whether the toggle worked. The JS code.
Setting up the Android tests
I followed this guide to some extent, although needed to work around a number of issues. Start off by adding the test runner dependencies to build.gradle
and re-syncing gradle.
Creating and running a basic test was actually a bit of a pain. In Android Studio move the cursor to the class you want to test and hit Command
+Shift
+t
and a dialogue will appear with “Create New Test…”.
The directory structure is actually when running the tests. Instrumented tests should end up in app/src/androidTest/
. Read more about android test directory structure.
You will also need to create a new configuration. This can be done through “Edit configurations” next to the Run button.
I created a configuration called InstrumentationTests
and set android.test.InstrumentationTestRunner
as the test runner for that configuration.
Using the UI Automator Viewer
The UI Automator Viewer was one of the cool things I discovered during this. It lets you take a ‘screenshot’ of your emulator and inspect the Views on the screen. It’s scope is the whole phone, so you can inspect things even outside of your app.
Click on different views from the screenshot and look at their respective properties.
Note: if you have trouble finding the UI Automator View, it is located in your Android SDK folder. If you have trouble finding your Android SDK folder, here is a Stack Overflow answer for you.
Properties that we can see that are particularly important for us as selectors are content-desc
and text
. Traditionally the resource-id
has been an important identifier as well, but there was a bit of discussion within the React Native community where they decided not to support it for now. Instead, when creating components on the React Native side, you will have to use the AccessibilityLabel
prop on views to set the content-desc
.
Writing the tests
You can take a look at the full test file here.
We use the UiDevice class to control the emulator, navigate to home, and open up the app.
// Open the simulator and head to the home screen
device = UiDevice.getInstance(getInstrumentation()); device.pressHome();
device.wait(Until.hasObject(By.desc("Apps list")), 3000);// Open up the list of apps
UiObject2 appsButton = device.findObject(By.desc("Apps list")); appsButton.click(); device.wait(Until.hasObject(By.text("TestAutomator")), 3000); // Open our app
UiObject2 testAutomatorApp = device.findObject(By.text("TestAutomator"));
testAutomatorApp.click();
Note: TestAutomator
is the name of my app. Also, I used to UI Automator Viewer to figure out that "Apps list"
was the content-desc
for the apps button.
In the tests, we use a BySelector
to select various elements on the screen. There are number of different selector methods. It’s more concise to use the content-desc
, but as you can see in the example above, I use By.text(“TestAutomator”)
to pick out the app by the text
in its name.
Here is a test I wrote to ensure that the green box appeared when the button was pressed:
BySelector greenBoxSelector = By.desc("greenBox");
BySelector buttonSelector = By.desc("toggleButton");// Find the toggle button and click it
device.wait(Until.hasObject(buttonSelector), 3000);
UiObject2 button = device.findObject(buttonSelector); button.click(); // Find the green box and assert it exists device.wait(Until.hasObject(greenBoxSelector), 3000);
Boolean greenBoxExists = device.hasObject(greenBoxSelector); assertTrue("The green box exists", greenBoxExists);
The UiObject2
class is used for interacting with selected objects. To check whether they exist or not, use device.hasObject
, which returns a Boolean
, rather than device.findObject
.
Another thing to take note of is the wait
. In automated tests there is often a delay for things to load or render so you have to wait or put a delay in otherwise your new view will not be found.
In addition to the one test ensuring that the box appears, to be on the safe side I also wrote one to ensure there is no green box at the start.
Running the tests
Now that I have my tests and configuration in place, I could select my configuration from the dropdown next to the “Run” button and hit run. Here are the two tests being run entirely automated.