Software Management (Part 3) — Testing
Testing the application with Jest and Detox
Be sure to also check out the other parts of this tutorial:
- Software Management (Part 1) — Getting Started
- Software Management (Part 2) — Continuous Integration
- Software Management (Part 3) — Testing
Finished product: https://gitlab.com/TimoGlastra/softwaremanagementapp
The steps we are going to walk through are:
- Setup TypeScript compiling to check for type errors
- Setup unit tests
- Setup native e2e testing for iOS and Android
Compiling Typescript code
We are going to automatically compile our TypeScript code when running our CI jobs to check for type errors.
TypeScript has a compiler that you can invoke with the
package.json file add the following to your scripts:
"types:check": "tsc -p tsconfig.json --noEmit"
-p tsconfig.json: TypeScript config file, generated by React Native Template TypeScript
--noEmit: Don’t output the compiled code, perfect for checking our code for type errors
You can now compile your code by running
yarn types:check. To make sure the type checking is working you can add something like
const d: number = "NAN"; to the
App.tsx file. If you now run
yarn types:check again it will give you an error:
We can also tell GitLab to compile our code as a CI job. Add the following job to
.gitlab-ci.yml. The only thing you really need to focus on is the
script tag that runs the
yarn types:check command.
If you’re curious for the result of this job, you can push your code to GitLab to see the job run. For me, it passed! :)
Sidenote: Babel / Jest / React Native config file conflict
Because of a bug in Babel / Jest / React Native the presets can’t all be read from the already existing
.babelrc file. To fix this issue, install the following packages:
yarn add --dev @babel/core firstname.lastname@example.org
And change / create your babel files to be exactly like below:
Setting up unit tests
Now let’s add some unit tests. React Native comes with Jest testing setup out of the box. Jest describes itself as a “Zero configuration testing platform”. It has a very nice feature called Snapshot testing, which takes an input and compares it to an earlier saved version of the snapshot. This way you can check if, for example, two buttons render exactly the same between two test runs. For more info about Jest, I suggest you read the documentation.
Because Jest is provided out of the box, just like adding TypeScript compiling, we are going to add a new script to
package.json and we’re ready to roll. See the Jest docs for the meaning of different parameters.
"test:jest": "jest src/ --coverage --verbose"
We also need to extend our Jest config to tell jest to also check for TypeScript files. Replace the current
jest key in
package.json with below code:
yarn test:jest we are able to run our tests. It will, however, not work yet because we don’t have any tests to test. That’s what we are going to do now.
To keep things from getting messy, make a
src folder, place the
App.tsx file in this folder and make sure to update the import to
index.js. To let TypeScript now we moved our files to a new folder add
"include": ["src"] to
tsconfig.json above the
now create a new file called
App.test.tsx in the src folder containing the following code.
The above code will test for two things.
- Renders correctly: Make a snapshot test to check if the component renders correctly
- Renders the correct amount of children: Check if the component has exactly three Text components inside of it. As you can see in
Appcomponent has exactly three
Textcompononents inside of it.
The Jest script will output the test coverage output to a
coverage/ folder. You don’t want this in your git version control, so I suggest you add the following to
Finally, we can test our code by running
Native testing with iOS and Android
The last part of testing our code includes running e2e tests to test the whole application at once. As mentioned earlier, GitLab has no way to natively test iOS applications. So we are going to set up our own GitLab runner.
I did consider using an external CI service for running our iOS and Android e2e tests, but there are already a lot of tutorials covering this subject. Besides that, we would have to maintain two different CI solutions for this super simple project.
Setting up a custom GitLab runner
GitLab runner is available for a lot of platforms. It is not required that you have OSX (I made sure this tutorial can be run on Linux / Windows), but it is needed to run the iOS tests.
Follow the steps for installing and registering a GitLab runner for your specific platform, as described in the docs. Use the following answers in the registering process to make sure everything gets set up correctly.
- coordinator URL:
- token: Go to
Settings > CI/CDand expand the
Runnerstab. Use the registration token provided here.
- description: Pick something you want, I used “Custom GitLab Runner”
react-native,ios,androidUsed to let GitLab know we want to use our own runner instead of the shared runners
If you followed all the steps of the GitLab documentation you should now have a registered, installed and started GitLab runner. You can verify this by going to your GitLab project then go to
Settings > CI/CD and expand the
Runners tab. Here you can see your custom runner.
Setting up Detox for cross-platform e2e tests (Needed for iOS and Android)
Detox is a library for testing React Native projects. Detox itself describes it as “Gray Box End-to-End Testing and Automation Framework for Mobile Apps”.
Install the following dependencies to your project:
yarn add --dev detox @types/detox
To bootstrap your project run the following command inside your project:
yarn detox init -r jest. This will create an
e2e folder with the required configuration files. It will also create a sample file
firstTest.spec.js, you can delete this file.
Setting up Detox for TypeScript takes some additional steps. Because we are also using Jest for our unit tests the typings between Jest and Detox are conflicting.
To fix this follow the next steps:
"types": ["jest", "react", "react-native", "react-test-renderer"]to the
- Create a new file
tsconfig.e2e.jsonand copy the following into it:
- Replace the contents of
e2e/config.jsonwith the following:
Setting up Detox for iOS (Only needed for iOS)
To use detox for iOS, you need to have OSX. If you don’t have OSX you can skip this part and go straight to setting up detox for Android.
Detox has its own documentation on how to install detox for iOS. Follow the ‘Prerequisites’ and ‘Step 1: Install dependencies’ from here.
After detox is correctly setup for OSX add the following to the
detox key in
package.json except that you need to change all occurrences of
SoftwareManagementApp with the name of your React Native application.
This will add configurations to run the tests in debugging and release mode. If you would like to know more about the configuration see the detox docs.
Setting up Detox for Android (Only needed for Android)
Setting up Detox for Android is a bit different than for iOS. There are also some differences between UNIX (Linux / OSX) and Windows. I will try to address the differences as best as possible.
First, start by following steps 1–3 from the setup Detox for Android docs. If this is all set up add the following to the
detox key in
package.json. Make sure you replace the REPLACE fields with the correct fields. It’s also possible to use GenyMotion on Windows, see the third configuration.
NOTE FOR WINDOWS: Windows can’t run executables starting with
./. So if you’re using Windows make sure the replace all the
./gradlew occurrences with
The configuration is provided, but can’t be run without setting up signing. Signing applications is a whole other topic, so I’m not going to explain how to do this. However, you could follow the Generating Signed APK tutorial from the React Native docs, after which you will be able to run this.
Writing our first test
We have everything set up now for (iOS and) Android. But we can’t run it yet without tests. So let’s create our first test.
App.tsxso that the
NOTE: Next time you run
yarn test:jest make sure you run it with
yarn test:jest -u to update the snapshots because we have changed
- Now create a new file called
This test does basically nothing. It only tests if an element with a testID of
app is visible. Which should be visible, as we added the testID to
Running our tests for the first time
In the previous step we have created our first test, now let’s test if this test will run.
Run for Android:
detox build -c android.emu.debug
detox test -c android.emu.debug
android.genymotion.debug if using GenyMotion.
Run for iOS
detox build -c ios.sim.debug
detox test -c ios.sim.debug
Running e2e tests as CI job
This is where it gets really interesting. Let’s add the e2e detox tests to our CI jobs.
Add the following two jobs to
e2e_ios job runs the release version of the application. The
e2e_android job runs the debug version of the application because of the signing issues.
Both jobs will first build the application (
detox build) and then run it (
detox test). The
--cleanup argument is for automatically stopping the simulator/emulator after the test has finished.
NOTE: The iOS build will exceed the log size limit. You can make this bigger in the settings of your custom GitLab runner. See this issue for more info. This does not mean the job will fail, it will, however, stop with outputting to the log.
In my case, after committing and pushing all the files, all the tests have passed 🎉🎉🎉
You can see the full pipeline on my GitLab account: https://gitlab.com/TimoGlastra/softwaremanagementapp/pipelines/38391359/builds.
All this set up work without real content is not worth a dime, so I hope you are going to build an amazing application with the fundamentals of this tutorial. I suggest you read some more into the documentation of all the different packages used. They are the best source for gaining more knowledge about the different subjects.
I really hoped you followed the tutorials without any problems. However, the combination of TypeScript / React Native / CI / Windows / OSX has given me trouble more often than I had hoped for. That’s one of the reasons I wrote these tutorials.
If you run into any problems, please let me know and I will be happy to help you.