An Introduction to React Native Testing with Cavy

I’ve recently been using Cavy to handle end-to-end testing for a React Native app. In this post, I’ll go over some first impressions and a few patterns I’ve found to be helpful.

Intro to Cavy

Cavy is an end-to-end testing framework that integrates with React Native using refs. The test suite runs as an app, using a root Tester component provided by Cavy. The root of your application is passed as a child to the Tester, and your test cases are passed as a prop.

Test cases are written as functions. They take in a spec object that provides methods for declaring tests and interacting with the UI.

If you follow Cavy’s README, getting set up is quick. It should feel very comfortable to anyone who’s familiar with React development.

Cavy CLI

When first using Cavy, you’ll see that tests are reported in your debugger, but not reported to the screen. This is probably fine for local testing, but it’s not robust enough for running tests in CI. Thankfully, the team behind Cavy also created cavy-cli, which allows reporting.

In case you missed it in the Cavy docs (as I did), when using cavy-cli, you’ll want to set the Tester’s sendReport prop to true.

This will enable test reporting in your terminal, as you’d expect from just about any other testing framework.

Challenges

It’s very easy to get started with Cavy, but it isn’t without challenges. Here are a few of the issues I’ve run into, along with some suggestions on how to deal with them.

Running your app without Cavy

If you follow the docs, you can set up your testing. However, you probably won’t be able to launch your app without the tests running, since the test component is now the root of your app. There are a number of ways you can remedy this:

  • Use environment variables to determine the root component of your app. This is pretty easy to set up, but because Cavy is still included, it’ll increase the size of your built app, which isn’t great.
  • Create separate index files (perhaps index.test.js and index.app.js), and copy them to index.js as part of the build process.
  • Use different index.js files for different build variants (testing vs. production). This is probably the cleanest solution.

Using refs means no FunctionComponents

I’m typically a big fan of using FunctionComponents whenever possible; they’re a bit cleaner to look at than class-based Components, and they emphasize that the component has no state, which often makes the component cleaner.

Cavy relies on refs to identify components. But FunctionComponents don’t have refs, so there’s a bit of a challenge there. Possible solutions include:

  • Write all components as class-based Components.
  • Use a function to convert FunctionCompopnents to Components. The cavy team suggests using recompose, but it doesn’t appear to be actively maintained anymore.

No support for TypeScript

If you’re using TypeScript, you’ll find that there are currently no type definitions. This means you’ll probably want to provide your own type definitions. Because Cavy’s very simple, this shouldn’t be too hard, but it does add overhead.

Conclusions

Cavy offers a nice solution for end-to-end testing of React Native apps. It’s quick to start and easy to use. It does come with some challenges, but they are not difficult to overcome.

Conversation
  • Abigail says:

    Hey! I’m a developer at Pixie Labs – we built and maintain Cavy :)

    We’ve actually been making some big improvements to Cavy over the last couple of months, addressing the challenges this post mentions –

    Running your app without Cavy:
    Using Cavy CLI with a `index.test.js` file is now the default way of running Cavy tests, meaning that your test suite will not run on app boot (unless you want them to!).

    Function component support:
    If you control the function component yourself, Cavy now has a custom React Hook `useCavy`. Call this within your function component to generate a ref to pass to inner components.
    If you don’t control the component you want to test, use Cavy’s `wrap` turn the function component into a class component and pass it a ref.

    TypeScript support:
    We’ve got type definitions! https://www.npmjs.com/package/@types/cavy

    Check out the rest of the documentation on our new site https://cavy.app/ !

  • Comments are closed.