Software Management (Part 3) — Testing

Testing the application with Jest and Detox

Be sure to also check out the other parts of this tutorial:

Finished product:

The steps we are going to walk through are:

  1. Setup TypeScript compiling to check for type errors
  2. Setup unit tests
  3. 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 tsc command. This will compile the TypeScript code to JavaScript. It’s very easy to get started. In your package.json file add the following to your scripts:

"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 babel-core@7.0.0-bridge.0

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 topackage.json and we’re ready to roll. See the Jest docs for the meaning of different parameters.

"scripts": {
"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:

By running 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 App in index.js. To let TypeScript now we moved our files to a new folder add "include": ["src"] to tsconfig.json above the "exclude" key

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 App.tsx, the App component has exactly three Text compononents 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 .gitignore:

# Jest

Finally, we can test our code by running yarn test:jest

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/CD and expand the Runners tab. Use the registration token provided here.
  • description: Pick something you want, I used “Custom GitLab Runner”
  • tags: react-native,ios,android Used to let GitLab know we want to use our own runner instead of the shared runners
  • executor: shell

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 toSettings > 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:

  • add "types": ["jest", "react", "react-native", "react-test-renderer"] to the compilerOptions key in tsconfig.json
  • Create a new file tsconfig.e2e.json and copy the following into it:
  • Replace the contents of e2e/config.json with 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 gradlew.

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.

  • Change App.tsx so that the View includes a testID attribute

NOTE: Next time you run yarn test:jest make sure you run it with -u as yarn test:jest -u to update the snapshots because we have changed App.tsx

  • Now create a new file called App.spec.ts inside the e2e folder.

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 App.tsx.

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

or replace android.emu.debug with 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 .gitlab-ci.yml

The 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.

Final results

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:

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.