React Native e2e testing with Cavy

Luiz Parreira
Mar 28, 2018 · 6 min read

How we at Magnetis Backstage are writing e2e tests for our mobile application

End to End testing is a hard problem in any stack, there are many moving pieces to any modern application that makes it very hard to do it properly. On React Native’s land it’s especially hard due to the fact that React Native works as a bridge between Javascript code and Native code, and many of the testing frameworks available work differently for Android and iOS, or do not work for one of the platforms at all. We wanted a solution that could be seamlessly used for both, and thats when my colleague Igor Fernandes watched a talk about Cavy.

Where the creators of cavy explain the philosophy and idea behind the framework.

In this blog post I will be describing how we at Magnetis Backstage implemented our cavy testing stack. I won’t touch on the CI part, but we have also implemented that on Bitrise for both iOS and Android.

  1. I will start by explaining what cavy is and what were my first impressions of it.
  2. Then I will show you how we solved the necessary problems in a way that did not corrupt our production code and made it really easy for us to make a component visible to cavy.

Besides having a cute name and logo, what does it do and how can it help us?

Cavy is a cross-platform integration test framework for React Native, by Pixie Labs

Cavy (ab)uses React ref generating functions to give you the ability to refer to, and simulate actions upon, deeply nested components within your application. Unlike a tool like enzyme which uses a simulated renderer, Cavy runs within your live application as it is running on a host device (e.g. your Android or iOS simulator).

This allows you to do far more accurate integration testing than if you run your React app within a simulated rendering environment.

I have to confess, at first when watching the video and reading the spike code written by my colleague I was a bit skeptical of whether this would be the best option to go with, as the documentation tells me that I have to wrap my app in a Tester component:

And once you have your app wrapped in that component, you have to go on to hook all your components that you want cavy to see by passing a prop generateTestHook to it and adding a ref prop to the component to be tested.

So that means that your pure production code should have to be wrapped in a hook function so that you can receive the generateTestHook prop. Also, you may notice on line 9 that if you have a pure functional component you have to wrap it in a class. I wasn’t very happy with that either: Come on, how on earth will I ship something to production that has so much code that has nothing to do with my business rules and the experience that I am providing to my user?

At the end of that day, when I went home, I remembered another very cool React feature Higher Order Components (HOC), we had been using recompose and its magic for a while now, so I started thinking of how could I combine both cavy and recompose, to be able to create a cleaner API to deal with these complexities.

By following these steps, you can integrate Cavy into your app, as they advise on their github repo:

  1. Wrap the app around the Tester component;
  2. Hook the components to inject the generateTestHook prop
  3. Wrap the functional components in a class

Now, I will show you how we integrated cavy into our app without changing almost nothing of our current code.

There are a few things we did to improve the implementation Cavy advises us through the docs:

  • Use react-native-config to create a .env.test configuration where we have an env var ENV=test and create a constant isTest .

In order to implement the Tester app, we created a component that uses the special prop children that renders the children of such components when it is rendered somewhere.

So, we would update our index.js to now use our isTest helper function and our new TestableApp .

Once we can identify when we are testing and also render our tester app only when we are in those situations, we can go on to implement our HOC that will make it really easy to be able to make the components you write available to cavy.

This component is called a testable and it receives the name of the reference, and takes care of hooking, injecting the prop and wrapping the component for you. It uses some functions provided by recompose , so you will want to integrate recompose into your project by running npm i recompose --save.

Now, lets say you have a HelloWorld component text that you want cavy to be able to see, because you want to make some assertions on it during testing. You could then use the above API to write:

And what the function testable is doing is creating a prop called ref={generateTestHook('HelloWorld.Text')} on this component, but on the context of when the component its being used, because we are following the API of the Higher Order Component (HOC), which is a Component => Component => Component and so on and so forth.

There is one caveat though, your components that perform actions, such as Inputs and Buttons, should follow the same pattern React Native imposes, so if you have a button, its action prop should be called onPress and if you have a text input, it should be called onChangeText following the same names React Native gives them. If you don’t do that, cavy won’t be able to call the function when it needs to click a button of fill in a text input.

Once we have that hooked, we can go on to write our specs that should look similar to this. And finally, run our tests by running:

# ios
$ ENVFILE=.env.test react-native run-ios
# android
$ ENVFILE=.env.test react-native run-android

Once the app builds, make sure you have toggled the debugger for both platforms, thats where the test results are printed.

As an end note, I would like to point out that despite its youth and simplicity, Cavy is a tool that will certainly help us maintain the quality expected across our iOS and Android apps. It is not enough though, we maintain a thorough coverage of unit tests and we plan on rolling out Enzyme tests as well in the near future. In addition to that, this task has shown us that with the compositional nature of React, you can almost always imagine a solution that can fit better into your needs and taste.


  • testable should become part of cavy in future versions with this Pull Request.
  • Cavy has a very good Example app available.

magnetis backstage

giving back to the community with things we learned at…