React Native Storybook + Generating individual test files to snapshot
I really enjoyed using React Native for the first time and my app is now in the app stores (yay!)
In this post I’ll describe how I got React Native working with Storybook and Storyshots plugin along with a custom script to generate a separate test file per story file. This in turn speeds up jest watching.
Firstly some background on this particular part of the stack. Storybook is a fantastic way to build your component library and becomes a living style guide. See https://storybook.js.org/
Then comes the question of unit testing these components too. The storybook addon storyshots is a great quick way to get some form of component unit testing going (https://storybook.js.org/testing/structural-testing/). Storyshots works by taking a snapshot (via jest + enzyme shallow render) of each of your stories automatically.
To set up storybook and storyshots in React you might dynamically require *.story.tsx
and pass that list into the storybook component. However React Native packager doesn’t support dynamic requires so you need to manually create a list of all required stories (Haul packager I understand does support dynamic requires which is cool). This script instead will generate a file listing all requires for you ready to import into your storybook component.
Then normally with storyshots you generate (eg with https://github.com/elderfo/react-native-storybook-loader ) or create a single test.ts file looping over all the stories and running a snapshot test on each story. More info in storyshots docs here: https://github.com/storybooks/storybook/tree/master/addons/storyshots/storyshots-core#configure-storyshots-for-html-snapshots)
The problem here is that any change to a component will trigger the jest watcher to re-run all the storyshot tests which can be slow. So the solution in this script is to generate multiple test.ts files adjacent to each story file so the watching is much quicker and only re-runs the storyshot test that has changed.
In future I’m actually moving away from shallow snapshot tests of my stories in favour of a proper unit test using a full render (aka mount) not shallow. An explicit unit test encourages the developer to specify intent rather than generating a brittle snapshot (eg expecting the render to contain 4 list items). A full render (as oppose to shallow) is also an improvement as more real code runs. This all would negate the use of storyshots, but I’d still use storybook as it’s an awesome visual tool.
This React Testing library (https://github.com/kentcdodds/react-testing-library) is an example of this but does not currently support React Native.
The only downside to this future direction is you would now have to write one story file and one test file for essentially the same states which is slower to write. If anyone has any thoughts on reducing this boilerplate do let me know. Perhaps a library like react cosmos is a solution to this (https://github.com/react-cosmos/react-cosmos).
Right on to some code (which one day I could make into a library?). The code is a quick work in progress. There are definite improvements to be made (like no global variables, better test template), don’t judge me :)
All the code below is written with Typescript by the way.
Here is a snippet of the package.json
where I’ve added the storybook libraries and the script commands to generate and run storybook.
The idea is to to just run yarn storybook
as normal and it will generate all test files before running storybook itself. By default storybook runs any prestorybook
command specified first, so we hook into that to run our own createTests.ts
script.
createTests.ts
firstly generates a file with a list of all stories for storybook to load and then it also generates one jest test file per storybook component. Note the extra tsc
typescript compiler step too as I like typescript.
The createTests.ts
script:
- Generates an
allStories/index.ts
file. Can commit to git. This is a list ofrequires
for your storybook component to load all stories in. - Generates one story test file (
<FooComponent>.story.test.ts
) per component adjacent to the story file (ie in the same directory). Again can commit these to git. - Deletes any orphan
story.test.ts
files if you delete a story file
Ok getting there, next is to load that allStories
into the react native storybook component. Import this method into your <App>
and add the component returned by this initStorybookUI
method to your root app somewhere. I add a button in the app in _DEV_ mode to quickly show/hide the storybook component for instance.
Finally here are some other files of note referenced in the createTests.ts
script.
Firstly the testTemplate.ts
the script reads as a template for each generated jest test file. Then an associated testStory.tsx
which is a test of this template really which jest will also run for you.
In the test template itself you want to setup some nice mocks to use in the jest snapshot test like mocking storybook decorators so they are not outputted in the snapshot
Then getting enzyme shallow render working with React Native was also tricky regardless of using storybook snapshots. So here is a jest.setup.js
file which does that.
Note that snapshot serialization with enzyme-to-json
package.
Reference this file in your jest.config.js
(or equivalent)
{
...
snapshotSerializers: ['enzyme-to-json/serializer'],setupFiles: ['<rootDir>/tools/jest/jest.setup.js'],
...
}
I think that’s it. This is a bit of a pain to get running but I found it helpful to have the jest snapshots of my stories as a form of unit test.
Do let us know if you have other/better ways of doing something like this and any feedback is great as I’d love to improve this.
✉️ Subscribe to CodeBurst’s once-weekly Email Blast, 🐦 Follow CodeBurst on Twitter, view 🗺️ The 2018 Web Developer Roadmap, and 🕸️ Learn Full Stack Web Development.