REGISTER

Run Tests for Key Pieces of Your App

Mike Grabowski
Jakub Mazurek
2022-06-06

blog content

Need help with Performance Optimization? 
hire us
Need help with Super app development
hire us
Need help with React Native?
hire us
Need help with migration to React Native?
hire us
Our React Native EU Conference is back
register nowlearn more

The following article is part of The Ultimate Guide to React Native Optimization.

Now let's talk about why you should focus on testing key pieces of the app to have a better overview of new features and tweaks.

Now let's talk about why you should focus on testing key pieces of the app to have a better overview of new features and tweaks.

Issue: You don’t write tests at all or write low quality tests with no real coverage, and rely only on manual testing.

Building and deploying apps with confidence is a challenging task. However, verifying if everything actually works requires a lot of time and effort – no matter if it is automated or not. Having somebody who manually verifies that the software works as expected is vital for your product.

Unfortunately, this process doesn’t scale well as the amount of your app functionalities grow. Neither does it provide direct feedback to the developers who write the code. Because of that, the time needed to spot and fix a bug is longer.

Automated tests

So what do developers do to make sure their software is always production-ready and doesn’t rely on human testers? They write automated tests. And React Native is no exception. You can write a variety of tests both for your JS code – which contains the business logic and UI – and native code that is used underneath. 

You can do it by utilizing end-to-end testing frameworks, spinning up simulators, emulators or even real devices. One of the great features of React Native is that it bundles to a native app bundle, so it allows you to employ all the end-to-end testing frameworks that you love and use in your native projects.

But beware, writing a test may be a challenging task on its own, especially if you lack experience. It’s easy to end up with a test that doesn’t have a good coverage of your features. Or only to test positive behavior, without handling exceptions. It’s very common to encounter low-quality tests that don’t provide too much value and hence won’t boost your confidence of shipping the code.

Whichever kind of test you’re going to write, be it unit, integration or E2E (short for end-to-end), there’s a golden rule that will save you from writing bad ones. And the rule is to “avoid testing implementation details.” Stick to it and your test will start to provide value over time.

You can’t move as fast as your competition, chances of regressions are high, your app can be removed from the app store when receiving bad reviews.

The main goal of testing your code is to deploy it with confidence by minimizing the number of bugs you introduce in your codebase. And not shipping bugs to the users is especially important for mobile apps, which are usually published to app stores.

Because of that, they are subject to a lengthy review process, which may take up to a few days. And the last thing you want is to frustrate your users with an update that makes your app faulty. That could lead to lowering of your rating and, in extreme cases, even taking the app down from the store.

Such scenarios may seem pretty rare, but they happen.Then, your team may become so afraid of having another regressions and crashes that it will lose its whole velocity and confidence.

Solution: Don’t aim at 100% coverage, focus on key pieces of the app. Use unit tests (Snapshots), integration tests (Detox).

Running tests is not a question of “if” but “how.” You need to come up with a plan on how to get the best value for the time spent. It’s very difficult to have 100% lines of your code and dependencies covered. Also, it’s often rather impractical.

Most of the mobile apps out there don’t need a full test coverage of the code they write. The exceptions are situations in which the client requires the full coverage because of the government regulations they must abide by. But in such cases you’re probably aware of the problem.

It’s crucial for you to focus your time on testing the right thing. Learning to identify business-critical features and capabilities is usually more important than writing a test itself. After all, you want to boost confidence in your code, not a write test for the sake of it. Once you do that, all you need to do is decide on how to run it. You have quite a few options to choose from.

In React Native, your app consists of multiple layers of code, some written in JS, some in Java/Kotlin, some in Objective-C/Swift and some even in C++, which is gaining adoption in React Native core.

Therefore, for practical reasons, we can distinguish between:

JavaScript testing – with the help of Jest framework. In React Native context if you think about “unit” or “integration” tests, this is the category they eventually fall into. From the practical standpoint, there is no reason for distinguishing between those two groups.

End-to-end app testing – with the help of Detox, Appium or other mobile testing framework we’re familiar with.

Since most of your business code lives in JS, it makes sense to focus your efforts there.

Testing pyramid. Source: Twitter/aaronabramov

JavaScript testing

Writing tests for utility functions should be pretty straightforward. To do so, you can use your favorite test runner. The most popular and recommended one within the React Native community is Jest. We’ll be referring to it also in the following sections.

For testing React components you need more advanced tools though. Let’s take the following component as an example:

It is a React component that displays a list of questions and allows for answering them. You need to make sure that its logic works by checking if the callback function is called with the set of answers provided by the user.

To do so, you can use an official react-test-renderer library from the React core team. It is a test renderer - and in other words - it allows you to render your component and interact with its lifecycle without actually dealing with native APIs. Some people may find it pretty intimidating and hard to work with, because of the low-level API.

That’s why the community around React Native came out with helper libraries, such as React Native Testing Library, providing us with a good set of helpers to productively write your high-quality tests.

A great thing about this library is that its API forces you to avoid testing implementation details of your components making it more resilient to internal refactors.

A test for the <rte-code>QuestionsBoard<rte-code> component would look as follows:

Test suite taken from the official RNTL documentation

You would first "render" the QuestionsBoard component with your set of questions. Next, you would query the tree by the accessibility role to access an array of the questions, as displayed by the component. Finally, you would set the right answers and press the "submit" button.

If everything goes fine, your assertion would pass ensuring that the "verifyQuestions" function has been called with the right set of arguments.

<p-bg-col>Note: You may have also heard of a technique called “snapshot testing” for JS. It can help you in some of the testing scenarios, when repetitive data is being asserted from the test. The technique is widely adopted in React ecosystem, because of a built-in support from Jest. But it’s a low-level API and should be avoided, unless you have a firm experience in testing.<p-bg-col>

If you’re into learning more about the snapshots, check one of the Rogelio’s (one of the Jest contributors) talks about snapshot testing and the project repository which can help you with testing differences between various states of data, including React Components.

E2E tests

The cherry on top of our testing pyramid is a suite of end-to-end tests. It’s good to start with a so-called “smoke test” – a test ensuring that your app doesn’t crash on a first run. It’s crucial to have a test like this, as it will help you avoid sending a faulty app to your users. Once you’re done with the basics, you should use your E2E testing framework of choice to cover the most important functionalities of your apps. These can be for instance logging in (successfully or not), logging out, accepting payments, displaying lists of data you fetch from your or third-party servers.

<p-bg-col>Note: Beware that these tests are usually a bit harder to set up than JS tests and also more likely to fail for various reasons, like networking, file system operations, storage or memory shortage, providing you with little information on why they fail. This particular quality of (not only E2E) tests is called “flakiness” and you should actively work to avoid it, since it lowers the confidence in our test suite. That’s why it’s so important to divide testing assertions into smaller groups, so it’s easier to debug what went wrong.<p-bg-col>

For the purposes of this section, we’ll be looking at Detox, which is the most popular E2E test runner within the React Native community and is part of the React Native testing pipeline. This way, we can ensure that our framework of choice is supported by the latest React Native versions, which is especially important with the potential changes that may be happening at a framework level in the future.

Before going any further, you have to install Detox. This process requires you to take additional “native steps” before you’re ready to run your first suite. Please follow the official documentation as these steps are likely to change in the future.

Once you have successfully installed and configured Detox, you’re ready to begin with your first test.

This quick snippet shown above would ensure that the first question is displayed. Before that assertion is executed, you should reload the React Native instance to make sure that no previous state is interfering with the results.

Note: When you’re dealing with multiple elements (e.g. in our case – a component renders multiple questions), it is a good practice to assign a suffix testID with the index of the element, to be able to query the specific one. This as well as some other interesting techniques are the official Detox recommendation.

There are various matchers and expectations available to help you build your test suite the way you want.

Benefits: You have a better overview of new features and tweaks, can ship with confidence and when the tests are green - you save the time of other people (the QA team).

A high-quality test suite that provides enough coverage for your core features is an investment in your team’s velocity. After all, you can move only as fast as your confidence allows you to. And the tests are all about making sure you’re heading in the right direction.

The React Native community is working hard to make testing as easy and pleasant as possible – for both your team and the QA teams. Thanks to that, you can spend more time on innovating and pleasing users with flashy new functionalities, and not squashing bugs and regressions over and over again.

Author:
Mike Grabowski
Co-founder & CTO of Callstack. Mike is a React Native core contributor and author of many libraries. When he isn't working, he is on a race track.
arrow icon
MORE posts from this author

Bundle React Native apps using Webpack features

Discover Re.Pack – a Webpack-based toolkit that allows you to build a React Native app with the full support of the Webpack ecosystem.

learn more

More posts from this category

Ensure your React components perform as intended as your app grows

Discover Reassure - our open-source library that allows you to run performance tests measuring the average rendering time of the in-app components.

business benefits

Performance Optimization

To stay competitive, you need a high-performing app. Improving React Native performance can bring your company many business and tech benefits. To learn more about it, check the page entirely dedicated to React Native Performance Optimization. Discover a real-life example of React Native optimization we performed for Aaqua, a Singaporean platform that enables global users to share their passion through groups.

Bundle React Native apps using Webpack features

Discover Re.Pack – a Webpack-based toolkit that allows you to build a React Native app with the full support of the Webpack ecosystem.

business benefits

Why React Native?

Building an all-in-one platform can bring your company a lot of business and tech benefits like seamless UX experience, global reach, brand growth just to name a few. To learn more about the benefits of using React Native to develop super apps, check out the MoMo case study. Where we helped improve app's performance by migrating architecture to Re.Pack.

stay tuned

Subscribe to our newsletter

You may unsubscribe from these communications at any time. For details see the Privacy Policy.