Working in React Native has been an amazing experience. Coming from React, the workflow has been nearly frictionless. There has, however, been one question lingering: How the hell am I going to test this?
Specifically, what is the best way to unit test my component logic? Testing JavaScript that compiles to native code using DOM based testing utilities won’t work. I can do my functional testing using Appium or the built-in Xcode UI tests, but I needed a way to unit test my components.
## Mocks to the rescue
It turns out that you can unit test React Native component logic in isolation using traditional JS test libraries by tricking React Native into returning regular React components instead of native ones.
Below is my shim for React Native components and methods that allowed me to render them in my test environment:
## Mocking React Native with Mocha
First I tried mocking all the things with Jest, mostly because that is what was recommended officially. I was able to prove the concept but there were a couple of drawbacks:
- Jest is unreasonably slow, at least compared to the alternatives
- We typically use a Karma/Mocha/Chai on web React projects at Formidable, so it’d be nice to continue that convention
After bidding Jest farewell, I had to figure out how this was going to work with Mocha. I couldn’t use a Karma/Webpack setup like I would on the web because React Native uses its own packager, so using Webpack would be a redundancy. This means I had to to write a compiler to a) transpile Babel code and b) resolve the React Native module with a mock version.
Mocha takes a — compiler argument that I used to create a custom module loader. I created one below that will transpile .js files and inject the mocked React Native methods into the react-native requires:
Now, any time a module in a test file requires react-native, it will get the shim instead.
## Putting it all together
Now I had a mock set up and a custom compiler in place, and I want to see it in action. My first order of business was to install the dependencies required to run the tests. To make things simple, I just ran:
```bash npm i --save-dev mocha chai sinon react react-dom react-addons-test-utils enzyme ```
To briefly review the role each tool plays in the testing architecture:
- Mocha: Javascript testing framework
- Chai: Assertion library
- Sinon: Stubs, Mocks & Spies
- Enzyme: React testing utility
- React Test Utils: React testing helpers
- React & ReactDOM: Used to mock React Native for testing
Next I had to set up a mocha.opts
file that is used to provide options to Mocha. In a /test
directory, I created the file mocha.opts
and added the following:
Next I created compile.js
and setup.js
in our test directory. The compiler code I wrote earlier went in compile.js
and I put the following code into setup.js
so that I could provide chai.expect
globally, mostly for ergonomics:
Next up, I created react-native.js
in a new mocks directory under /test
at /test/mocks/react-native.js
and put the React Native mock code from earlier in there.
The final piece of the puzzle is pointing my test npm script at Mocha with the specified options. I opened package.json
and changed test under scripts to:
```bash "test": "mocha --opts test/mocha.opts src/**/__specs__/*.spec.js" ```
In addition to pointing to the defined options, I also set the target to tests that use the `*.spec.js` convention, located in folders named __specs__
.
Using this convention, I colocate my tests in the folders that contain the components that I’m testing, avoiding lengthy file paths when requiring our components.
Now it’s time to test!
## Getting my test on
Say we have a simple React Native component that we want to test to make sure that it renders child nodes in response an items prop:
We want to start by creating a __specs__
directory in src/components/
. Inside of that directory, we create our-component.spec.js
as our test spec for the above component.
For this test, we will be using shallow rendering, which renders components one level deep and lets us assert against the virtual DOM structure. We will also be using the enzyme library from the fine folks at Airbnb, which is an amazing utility for working with shallow renders.
Let’s start by importing React, enzyme and our component, and creating a describe grouping:
Now that we have our stage set, let’s create some mock prop data, shallow render our component, and use enzyme’s findWhere
method to see if everything is in working order:
Now, all that’s left to do is run npm test
and let the magic happen. We are unit testing React Native components!
## Wrap Up
I’ve found this approach to be pretty flexible so far, and I am feeling much better about the sturdiness of my React Native code. You may have noticed that in the React Native mock above, I had only mocked out a subset of the functionality. Using this approach may require mocking out additional components and methods depending upon what you are using in your app. I also recommend exploring all the features of enzyme, as it has an elegant API for traversing and querying a ShallowRender objects. Now go write some tests!
Thanks to Ryan Roemer and Alex Lande.