React Native development tools - Part 3: Testing tools
Knowledge of developing React Native apps Knowledge of editing config files for Android Development environment setup for React Native
This is the final part of a three-part series on React Native development tools where we cover the following tools:
- Part 1: Linting tools
- Part 2: Debugging tools
- Part 3: Testing tools
In this part, we’ll cover the following testing tools:
The goal of this tutorial is to get you up and running with these tools quickly. So expect a lot of setups. Aside from that, you’re also going to see these tools in action. We’ll be using a pre-created React Native app to implement the tools.
What we’re not going to tackle is how you’re going to design your code so it is easy to test. We’re also not going to tackle every feature the tools have to offer. At the end of the tutorial, I’ll be pointing out to a few resources so you can learn more about them.
Prerequisites
To follow this tutorial, you need to have knowledge of developing React Native apps. Optionally, you should also be comfortable with editing config files for Android, as we will be doing a lot of that in this tutorial. We don’t have to do a lot of that in iOS since Detox is more matured on iOS.
To follow this tutorial, you’ll need a computer running Linux or MacOS. I’ve personally tested this tutorial on the following operating systems:
- Ubuntu 16.04
- MacOS High Sierra 10.13.5
For Windows, I can’t ensure it will work. But the official docs did mention that the tools should also work on Windows.
Lastly, this tutorial assumes that you have already setup your machine for React Native development.
Setting up the project
Now that that’s out of the way, let’s go ahead and create a new React Native project for the purpose of using the testing tools:
git clone https://github.com/anchetaWern/RNTesting.git
git checkout starter
Take note of the React Native version we have on the package.json
file. It’s at version 0.50 even though at the time of writing this tutorial, the latest version is already at 0.56. We’re doing this because Detox is only proven to work for these versions of React Native:
- iOS: <=0.55
- Android: <=0.51
In order to target both platforms, we have to downgrade to the lowest of the two. In this case, it should be 0.51. While I was testing it though, I had problems getting Detox to work for React Native version 0.51. That’s why we’re sticking with version 0.50 instead.
Ideally, we want to use the latest version of React Native. But with the current state of Detox, it’s simply not possible. That doesn’t mean that you also have to downgrade the version of your existing project though. You can maintain another copy for testing purposes only. And then gradually upgrade it as support for later React Native versions becomes available.
All the changes introduced in this tutorial are available on the testing
branch. You can check that out anytime you feel confused about which specific change you need to implement.
Upgrade to Gradle 3
The next step is to upgrade the Gradle version used by React Native. By default, it still uses version 2, and so do most of the native modules that you’ll find on GitHub (except for projects like React Native Maps). If you’re worried that you’ll no longer be able to use the native modules that still use Gradle 2, then fret not because there’s a way to update those.
So why do we need to upgrade to Gradle 3? That’s because Detox also uses Gradle 3. There’s a way to stick with Gradle 2 but new features that will come to Detox won’t be implemented in there because it uses Detox version 6.
Let’s go ahead and start updating the config files so we can upgrade to Gradle 3. First, update android/build.gradle
with the following:
buildscript {
repositories {
jcenter()
google() // add this
}
dependencies {
// classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.android.tools.build:gradle:3.1.0' // replace the above with this
}
// the rest of the existing config here...
}
allprojects {
repositories {
mavenLocal()
jcenter()
google() // add this
maven { ... }
// the rest of the existing config here...
}
Since this is a new project, your config file will pretty much look the same as mine. If you’re updating an existing project, it’s going to be a bit different, but the overall structure should still be the same.
Next, update android/gradle/wrapper/gradle-wrapper.properties
with the new distributionUrl
:
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
Don’t be confused by the URL when it says gradle-4.4 while we want to use version 3.1. Because we need to set this to a higher version than what we want to use.
Once that’s done, execute react-native run-android
to install the new Gradle and verify that the app is working correctly. Don’t forget to launch an emulator instance (either the default Android emulator or Genymotion), or connect an Android device before doing so. Remember that we’re testing though, so practicality-wise, the devices we’ll be running the tests on are just virtual (Android emulator, iOS simulator).
That’s all there is to it when it comes to setting up the project. As you have seen, it’s only the Android config which took up most of the time. For iOS, you don’t really need to perform any additional steps in order to prepare the project for Detox and Jest. As long as you have the latest version of Xcode installed, you should be fine.
Setting up Jest
Jest is the tool that we’ll be using to implement snapshot testing. The main idea behind snapshot testing is that it allows you to test whether a component has changed. The workflow goes something like this:
- Implement the components.
- Write tests that will verify if your component is rendering correctly.
- Run the tests. This will create a
yourTestName.snap
file for each of your test files inside the_tests_/_snapshots_
directory. These files contain what’s actually being rendered by the components you are testing. We’ll go through this in more detail on a later section. - Commit the changes to your version control system.
- Every time you make a change, you run the tests again. The tests which already have
.snap
files will have their output compared to that existing snapshot file. If they don’t match, then there might be an unexpected change with your code. Once you’ve verified that the change is correct, you can either update the existing snapshot or revert the changes you made to your component. This way, the tests will pass again.
As you can see, snapshot testing isn’t test-driven development. Mainly because you have to write the component first before taking a snapshot. Snapshot testing is mainly used for checking for unexpected changes in your code, and not for directing how you design your code.
Configuring Jest
When you create a new React Native project, it already comes with Jest pre-installed and configured. Though you’ll still have to perform a few steps to ensure it will work nicely with the other tool that we’ll use (Detox).
On your package.json
file, you can see the default configuration options for Jest:
"jest": {
"preset": "react-native",
},
Update it to look like the following:
"jest": {
"preset": "react-native",
"testMatch": [
"<rootDir>/__tests__/*"
]
},
This tells Jest to only look for test files inside the _tests_
directory. Because by default, it’s going to run every file that looks like a test file. This means, that when we configure Detox later, it will run Detox’s test files as well. We don’t really want that to happen because they are testing two different things.
Setting up Detox
This section will pretty much go over the same things as the official guides:
In this tutorial though, I’m going to cover some details that aren’t covered in those guides.
With that out of the way, let’s begin.
If you only want to test on Android, you’ll only need to globally install the following:
- Node 8.3.0 - higher version of Node may also work, but I haven’t personally tested it.
- Detox CLI
I assume that you have already installed Node by some means. I personally use nvm to manage Node versions. So I installed the required version using the following commands:
nvm install 8.3.0
nvm alias default 8.3.0
nvm use default
Once that’s done, verify if version 8.3.0 is being used:
node --version
After that, you’ll need to install the react-native-cli if you’re using some other Node version before you installed 8.3.0:
npm install -g react-native-cli
Also install Yarn, we’ll be using it to install the local dependencies for the React Native project:
npm install -g yarn
Lastly, install the Detox command line tool:
npm install -g detox-cli
iOS setup
In order to test in iOS, you still need to have a Mac. I assume you already have brew installed so you can simply execute the following commands to install the Apple simulator utilities. Detox uses it to communicate with the iOS simulator:
brew tap wix/brew
brew install applesimutils
Setting up Detox in the project
Now we’re ready to install Detox in the project:
yarn add detox@8.0.0 --dev
yarn add mocha@4.0.1 --dev
We’re installing specific versions because Detox is a relatively new technology. I can’t ensure that this guide will work for you if you installed another version. If you’re just getting started, I recommend you to stick with the versions that we have here to avoid unnecessary headaches. For production cases, you’ll want to use the latest stable version.
Note that Mocha is the test-runner that we’ll be using. But you can also use Jest if you want. Yes, it’s the same thing that we’ll be using for snapshot testing because Jest is a testing framework, it includes everything you need for testing.
Android setup
Follow this section if you want to test for Android, otherwise, proceed to the next one.
The first step is to update android/settings.gradle
file to include Detox:
rootProject.name = 'rntdd'
include ':app'
# add the below config
include ':detox'
project(':detox').projectDir = new File(rootProject.projectDir, '../node_modules/detox/android/detox')
Next, under android
, update the compileSdkVersion
, buildToolsVersion
to version 27
. Then under android → defaultConfig
, set minSdkVersion
to 18
and targetSdkVersion
to 26
:
// file: android/app/build.gradle
android {
compileSdkVersion 27 // make sure this is 27
buildToolsVersion '27.0.2' // make sure this is 27.0.2
defaultConfig {
applicationId "com.rntesting" // make sure this is the same as the project name you used when you generated the project with react-native init
minSdkVersion 18 // make sure this is 18
targetSdkVersion 26 // make sure this is 26
// next: add testBuildType, missingDimensionStrategy, and testInstrumentationRunner
// rest of the config here...
}
}
Next, add the following under android → defaultConfig
:
testBuildType System.getProperty('testBuildType', 'debug')
missingDimensionStrategy "minReactNative", "minReactNative46"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Here’s a breakdown of what we’ve just added:
testBuildType
- the build type to be used for testing. This can either bedebug
orrelease
. In this case, we’ll be usingdebug
since we’re still developing the app.minReactNative46
- the minimum React Native version supported by Detox. In this case, the project uses version 0.50 so we need to useminReactNative46
to specify Detox support for React Native version’s that are greater than or equal to version 0.46.testInstrumentationRunner
- the native module used for running the tests.
Next, update the dependencies
section and add the following. These are Gradle 3 specific configuration, so if your project uses Gradle 2 then you’ll get an error if you add these. These are used to compile the native Java classes that are needed to implement the tests:
dependencies {
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
// add only the following. The above one's already exists
androidTestImplementation(project(path: ":detox"))
androidTestImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test:rules:1.0.1'
compile "com.facebook.react:react-native:+" // existing
}
Next, update android/build.gradle
to include the following under allprojects → repositories
(right after the maven
config):
buildscript {
repositories {
google()
}
}
Lastly, create an android/app/src/androidTest/java/com/rntesting/DetoxTest.java
file and add the following. This is the test class that Detox will be using. Be sure to replace “rntesting” in both the file path and the package name below with the actual name of your project if you’re using another one:
// file: android/app/src/androidTest/java/com/rntesting/DetoxTest.java (note: replace rntesting in this file path with the name of your project if it's different)
package com.rntesting; // replace with the actual package name
import android.support.test.filters.LargeTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import com.wix.detox.Detox;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class DetoxTest {
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
@Test
public void runDetoxTests() throws InterruptedException {
Detox.runTests(mActivityRule);
}
}
Adding the Detox config
Once installed, you can now update the package.json
file to include the detox
config:
{
"name": "RNTesting",
// the rest of package.json here...
"detox":{
"configurations":{
"ios.sim.debug":{
"binaryPath":"ios/build/Build/Products/Debug-iphonesimulator/rntdd.app",
"build":"xcodebuild -project ios/rntdd.xcodeproj -scheme rntdd -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type":"ios.simulator",
"name":"iPhone 7"
},
"android.emu.debug":{
"binaryPath":"./android/app/build/outputs/apk/debug/app-debug.apk",
"build":"cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
"type":"android.emulator",
"name":"Nexus_5X_API_25_x86"
}
},
"test-runner":"mocha",
"specs":"e2e",
"runner-config":"e2e/mocha.opts"
}
}
Breaking down the above config, every setting that has to do with Detox should be inside the detox
object. Under the configurations
we have the settings to be used by Detox to run the app on the iOS simulator and Android emulator.
Both platforms (Android and iOS) have the following options:
binaryPath
- the path to where the compiled React Native app is located. These paths are standard to both platforms so you don’t really have to change anything.build
- the command for building the app to be used for testing.type
- the device type. On iOS, the standard isios.simulator
. While on Android, it depends on what you use for testing. If it’s the Android emulator installed using AVD, the value should beandroid.emulator
. But if you’re using Genymotion, thetype
should beandroid.attached
. If this is the case, the value of thename
should be the IP address and port number of the virtual device. The most common value for this is192.168.57.101:5555
, but you can use theadb devices
command to find out. Don’t forget to boot up the virtual device first before doing so.name
- the name of the device. On iOS, you can find out the device names you can use by executingxcrun simctl list
. While on Android, you can executeavdmanager list avd
to get a list of Android emulators installed on your system. If that doesn’t work, you need to find out where Android SDK is installed. After that, go to thesdk/tools/bin
directory and execute./avdmanager list avd
. Here’s what the output for those two commands looks like:
Initialize testing code
Once you’ve added the config, the next step is to initialize the project for testing. For Mocha, you use the following command:
detox init -r mocha
This command creates an e2e
directory at the root of the project. The directory contains the following files:
init.js
- contains the initialization code that runs before any of your tests are executed.mocha.opts
- contains the options for running the tests.firstTest.spec.js
- the default Detox test file. Open it so you have an idea what a test file looks like. Because that’s what we’re going to work with later.
Compiling the app
The next step is to build the app so that the binaryPath
that we added in the Detox config earlier will actually have a runnable app:
detox build -c ios.sim.debug
detox build -c android.emu.debug
The command above compiles the debug version of the app for the configuration (-c
or --configuration
) that you specified.
That should work, but in case it doesn’t, try running the project by normal means to see if there are any issues that arise:
react-native run-android
react-native run-ios --simulator="iPhone 7"
If you get an issue which looks like this on Android:
That’s because the main app uses a different version of the support-annotations library from the one used by the test app. You can solve it by updating the android/app/build.gradle
file and set a configuration for a resolution strategy. This tells gradle to use version 23.0.1
of support-annotations in case there’s a conflict:
configurations.all {
resolutionStrategy.force 'com.android.support:support-annotations:23.0.1'
}
// existing android config
android {
...
}
Running the default test
The final step is to actually run the test:
detox test
Note that you can also add the -c
flag here to only run the test on a specific device. This is especially useful if the machine you’re testing on is not so powerful:
detox test -c android.emu.debug
detox test -c ios.sim.debug
If that doesn’t work and you get an error which looks like this:
That means that you need to update the node_modules/detox/src/devices/android/ADB.js
file. This file contains all the commands that Detox is executing to manipulate the emulator. Look for the pidof
method and replace the first command which looks like this:
async pidof(deviceId, bundleId) {
const processes = await this.shell(deviceId, `ps -AMo NAME,PID`);
// rest of the method here...
}
With this:
async pidof(deviceId, bundleId) {
const processes = await this.shell(deviceId, `ps`);
// rest of the method here...
}
We need to do this because certain Android versions. Those that have API version 25 and below doesn’t seem to recognize the ps -AMo NAME,PID
command. That’s why putting just ps
works.
Save the file and run the test command again. That should solve the issue. This time though, you’ll get another issue:
This one is completely normal because Detox is running the test located at e2e/firstTest.spec.js
. If you open it, it should contain three assertions for elements which don’t exist in the app. That’s why it’s failing. We’ll work on this on the Detox in action section. But first, let’s look at how we can work with Jest to implement snapshot testing for the app.
For Windows users who gets an error similar to the following:
This error is due to Windows not being able to properly parse the transfer of commands from Detox. So the solution is to just manually execute the build
command you have on the package.json
file:
cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..
If this doesn’t work, try to execute the commands one by one. Also, I recommend you use Git Bash if you keep getting errors.
If you’re having problems getting the above setup to work, I recommend you to go through the following troubleshooting guides first:
- Troubleshooting installation
- Troubleshooting running tests
- Troubleshooting synchronization
- Troubleshooting flakiness
Once you’ve exhausted those, search for the specific issue you’re having on the issues page or Stack Overflow.
Jest in action
As mentioned earlier, a React Native project already comes with a test which you can readily execute. Go ahead and try running it while on the root directory of the project:
yarn test
That should give you the following output:
This means that the test has passed. By default, it will pass. But as you make changes to your codebase, there will be some unexpected changes. That’s what Jest is meant to test, unintentional changes which break the standard that you’ve initially set for the code.
Solving asset transform issue
Note that if you have an issue which looks like the following. It means that the image is being treated as a JavaScript module instead of an asset:
To solve this, create a fileTransformer.js
file at the root of the project directory and put the following. This returns the path of the asset, in this case, an image. Instead of its source:
// fileTransfomer.js
const path = require('path');
module.exports = {
process(src, filename, config, options) {
return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
},
};
Then in the package.json
file, update the jest
config to use the fileTransformer.js
file for transforming the assets used in the project (I’ve gone ahead and included every image file extension, although we’re only using .jpg
in this project):
"jest": {
"preset": "react-native",
"testMatch": [
"<rootDir>/__tests__/*"
],
// add the following:
"transform": {
"\\.(jpg|jpeg|png|gif)$": "<rootDir>/fileTransformer.js"
}
},
// the rest of package.json
Once you’ve added that, the tests should now run.
Before we write some tests for the app, let’s first try out if Jest is really working as expected. Since we already ran the test, Jest should have stored a snapshot of that file inside the _tests_/_snapshots_
directory. The filename should be the same as the name of the test, but with an additional .snap
extension. So for the default test, there should be an App.js.snap
file. If you open it, you should see the render tree of the App
component.
Let’s prove that the test will fail if we make a change to any child of the App
component. Open the src/components/PokemonLoader.js
and set the background color:
const styles = {
textContainer: {
flex: 1,
alignItems: "center",
backgroundColor: "#fff", // add this
},
// the rest of the styles here...
}
Then on the _tests_/App.js
file, replace the default test with the following:
it("App is rendered correctly", () => {
const tree = renderer.create(<App />).toJSON();
expect(tree).toMatchSnapshot();
});
The code above checks if the render tree of the App
component matches the one in the App.js.snap
file.
To verify, run the test again with yarn test
and you should see an output similar to the following:
As you can see from the above screenshot, the test is failing, and it shows exactly which specific line caused the failure.
If the change you’ve introduced is correct, you can update the snapshot by adding the -u
flag to the command:
yarn test -u
This updates the .snap
file to add the backgroundColor: "#fff``"
style declaration. At this point, you can then commit both the component and snap file to version control.
Adding a test
Now it’s time for us to add a test. But before we do that, it’s important to have an understanding of how the app works. So in case you haven’t tried it yet, now is the perfect time to run the app:
react-native run-android
react-native run-ios
Try it out, and take note of how it works. Then think of how you can say the app is working correctly.
Since this is just a tiny app, we’re going to add the test inside the _tests_/App.js
file. For larger apps though, you might want to have a separate file for each page of the app, or group the tests based on related functionality.
The first and last thing that we’re going to test is whether the Card
component renders correctly and consistently, given the same data:
import Card from "../src/components/Card"; // the Card component from the app
import pokemonData from "./data/pokemon.json"; // the data to be used by the card
it("card renders correctly", () => {
const tree = renderer
.create(<Card fetching={false} data={pokemonData} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
As you can see, this follows the same structure as the sample test from earlier. The only difference is that we’re testing a specific component that’s rendered inside the main one. Also, we’re hardcoding the props that are normally passed in through mapStateToProps
.
You can find the contents for the pokemon.json
file here. Place it inside the _tests_/data
folder.
Other parts of the app which you might want to test are the reducers. To verify if the way they specify how the state will change is correct, given a specific action.
You can also interact with the components (for example: click a button, enter a text on a text field) and verify if their state is updated correctly. I’ll be leaving some links in the last section for further reading.
Detox in action
Detox is used for automating the tests that real users would normally do. If you’re coming from a web development background, Detox’s counterpart is Selenium. The workflow looks something like this:
- Write code that will automate user interactions. Things like clicking a button, or typing some text in a text field.
- Write code that will verify that the desired effect of that action is met. For example, when a user clicks a button, you want a specific text to show on the screen.
- Run the tests to verify the functionality.
Adding the tests
Now we’re ready to write some tests. This time, we’re going to test the following:
- Verify if the Pokeball image is rendered on the screen when the app is first loaded.
- Verify if the relevant Pokemon data is rendered on the screen after the user clicks on the button for loading a new Pokemon.
The tests are inside the e2e
directory. As mentioned earlier, there’s already a test file (e2e/firstTest.spec.js
) created for you. Rename the file to pokemonLoadingTest.spec.js
, open it and change the description:
describe("Pokemon data is loading", () => {
// rest of the code here...
});
The name of the file and the description should match the specific functionality (or group of functionalities) that you want to test. In this case, we’ll simply include all the tests in a single file since the functionality we’re testing is all related.
The first bit of code you will see after that is telling Detox to reload the React Native app. Note that this does not have a similar effect to when you reload the app using the ⌘ + r
, rr
, or Ctrl + r
on the Android or iOS simulator. So if you made some changes to the code, it wouldn’t actually trigger the watcher to recompile and update the app for you. This method runs before running any of the tests that will be included in this file:
beforeEach(async () => {
await device.reloadReactNative();
});
Now, let’s add the first test. That is, to verify if the Pokeball image is being rendered. Because that’s the only visible element which the user can click when the app is first loaded. The easiest way to target it is by adding a testID
prop. Use a unique ID that describes what the element is:
// src/components/PokemonLoader.js
{!pokemon && (
<Image
source={pokeball}
resizeMode={"contain"}
style={styles.pokeBall}
testID="pokeball_image"
/>
)}
Then in the test file, add the test for verifying if the component with that specific ID is being rendered and is visible on the screen. Note that if the component is under the non-visible area of the screen, the test will fail even if it’s rendered:
// e2e/pokemonLoadingTest.spec.js
beforeEach(...);
// add this
it("should show Pokeball image on app load", async () => {
await expect(element(by.id("pokeball_image"))).toBeVisible(); // 75% of the tested component should be visible on the screen
});
At this point, you can already run the test:
react-native start # run the watcher
detox test -c android.emu.debug # or ios.sim.debug
Note that we have to run the watcher first before running the test. Since we’re in debug mode, the app wouldn’t actually work if the watcher isn’t running. Also, if you’re testing on iOS, running the test doesn’t automatically open the iOS simulators UI (although it’s already running in the background). So if you want to see the actions being performed, you have to execute open -a Simulator
on your terminal.
Once that’s done, Detox should uninstall the app (if it already exists), re-install it, launch it, and then run the tests. The result should look something like this:
Since we’re in debug mode, and any changes you make to the code will get recompiled by the watcher, you can actually just reuse the installed app. This bypasses the uninstalling and re-installing of the app and immediately proceeds with launching it and running the tests:
detox test -c android.emu.debug --reuse
Though if you make changes to the code, don’t forget to reload the app so the watcher recompiles the code.
Let’s proceed with the final test. In this test, we want to verify whether all the relevant Pokemon data shows up in the screen when the user clicks the button for loading a new Pokemon. Update the src/components/Card.js
and add a testID
to the components which display all the relevant data. This includes the sprite, name, types, and description of the Pokemon:
<Image
style={styles.cardImage}
source={{ uri: image }}
resizeMode={"cover"}
testID="pokemon_sprite"
/>
...
<Text style={styles.title} testID="pokemon_name">
{name}
</Text>
...
<View
style={styles.typesContainer}
testID="pokemon_types"
>
{this.renderTypes(types)}
</View>
...
<Text
style={styles.description}
testID="pokemon_description"
>
{description}
</Text>
You also need to update src/components/PokemonLoader.js
to add a testID
to the button for loading the Pokemon:
<TouchableOpacity
onPress={requestPokemon}
testID="action_button"
>
Next, add the test to the test file:
// e2e/pokemonLoadingTest.spec.js
it("should show relevant Pokemon data after clicking the button and loading the data from the API", async () => {
await element(by.id("action_button")).tap(); // click the button
// verify that the components showing the relevant data exists
await expect(element(by.id("pokemon_sprite"))).toExist();
await expect(element(by.id("pokemon_name"))).toExist();
await expect(element(by.id("pokemon_types"))).toExist();
await expect(element(by.id("pokemon_description"))).toExist();
});
From the code above, you can see how simply it performs actions on a specific element. After the action is performed, we can immediately verify if the expected components are rendered in the screen. Previously, we’ve used toBeVisible()
, this time, we’re using toExist()
instead. This is a more relaxed way of checking whether the component exists. As the name suggests, the component doesn’t have to be visible in the screen for the test to pass. All it verifies is whether it is rendered or not.
Detox does all the magic required to keep the tests and the current state of the app in sync. What this means is that Detox knows when the app is busy making a network request, or any other non-trivial processing. Then once that process is done, that’s the only time that it runs the test.
Check out the following docs to learn more:
- Matchers - ways for targetting a component.
- Expectations - assertions.
- Actions - actions and user gestures that can be performed on a component.
Further reading
Here are some resources for your continued journey on testing React Native apps:
- Snapshot testing
- Testing React Native apps
- Snapshot Testing React Components with Jest
- Testing in React Native: Jest & Detox
- Learning to test React Native with Jest
- Detox official docs
- Detox: Gray Box End to End Testing Framework for Mobile apps
- End-to-end testing on React Native with Detox
Conclusion
That’s it! In this tutorial, you’ve learned how to test your React Native apps. Specifically, you’ve learned how to use Jest and Detox to implement snapshot and end-to-end testing.
As you have seen, Jest is a developer-ready tool while Detox is still getting there, especially on Android. Though, that shouldn’t stop you from using the tool because it’s continually being developed.
You can view the full source code used in this tutorial on its GitHub repo.
That wraps up the series! In this series, you’ve learned how to lint, debug, and test your React Native project. I hope you’ve gained the necessary skills for improving your React Native code by means of tooling.
25 September 2018
by Wern Ancheta