Visual unit testing with react-storybook and fetch-mock

Nom nom, croissant, nom nom

Working as a frontend developer in a project using React, suppose, while holding your cuppa, you pick this quite unlikely user story from the backlog:

As a user I want to see the bytes per programming language in the React repository.

Put your cup down, knowing in your heart that you will forget it and it will die a sad cold death.

Break down this story into tasks. The story roughly requires to write a React component that:

  • queries the GitHub API to fetch the needed information;
  • displays such information in a beautiful way.

As we are dealing with a network requests, it branches into at least three cases:

  1. A successful and quick response (less than 200ms).
  2. A successful and slow response (more than 200ms).
  3. Some kind of error (401, 403, etc.).

Moreover, the component is likely to be part of a much bigger codebase. Since we want to achieve fast visual feedback, developing the component inside the main application doesn’t seem a viable option. In a way, we want to setup a quick visual unit test, and only when we’re satisfied with it, run a slower visual integration test, assessing that our new component works well with the rest of the application.

Setting up

Let’s start by creating a new React application, using create-react-app, which surely helps reducing the much dreaded JS fatigue:

→ npm i -g create-react-app
→ create-react-app fetch-mock-storybook
→ cd fetch-mock-storybook
→ npm start

If everything worked fine, visiting localhost:3000, we should be welcomed with a nice landing page featuring a cool spinning React logo. It doesn’t spin here as I couldn’t be bothered to create a GIF.

react-create-app landing page, no spinning

Write some code

Time to introduce regressions and break production! Ehm, time to code!

We will write a very simple React component, which fetches via the GitHub API the number of bytes per programming language used in the React repository. At this moment we’re not interested in handling slow responses or error codes: just grab some data and display it.

The result is amazing. Honestly, what’s more appealing than a bullet list?

Display the total number of bytes per language used in the React repository

Ok, the requirements are (kinda) met. We still need to take care of slow or erroneous network responses. We will now stop coding in the main application though, as we isolate the component in its own story, and implement the remaining cases.

Meet Storybook

react-storybook is truly a fantastic tool. It allows us to develop and render our React components in isolation from the main application.

To add storybook to our example project, we first install globally getstorybook, then run it inside our project folder.

→ npm i -g getstorybook
→ getstorybook

getstorybook, among other things, will:

  • create the src/stories folder, where our stories will be loaded from;
  • add the storybook and build-storybook scripts in package.json.
{
"name": "fetch-mock-storybook",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^15.4.2",
"react-dom": "^15.4.2"
},
"devDependencies": {
"react-scripts": "0.9.5",
"@kadira/storybook": "^2.21.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"storybook": "start-storybook -p 9009 -s public",
"build-storybook": "build-storybook -s public"
}
}

Once getstorybook is done, we can start storybook by running:

→ npm run storybook

If we visit localhost:9009, we shall see the welcome page:

The welcome page of react-storybook

We can now modify .storybook/config.js to automatically load as stories all the .stories.js files in src/stories:

Remove everything in src/stories, and add App.stories.js

Stop and start againnpm run storybook and behold the App component loaded in our storybook.

The App component is now rendered inside the storybook

Nice! We are now independent from the main app, yet, we still depend on the actual GitHub API to fetch the data. In the next chapter of this incredible programming story we will mock the fetch out of this component!

Mock that API

We are going to use fetch-mock, a splendid library that allows us to mock the fetch API on both browser and server. First things first, install the library as a developer dependency:

→ npm i --save-dev fetch-mock

Then we can copy the payload of a successful response from https://api.github.com/repos/facebook/react/languages and paste it inside our story.

The payload from https://api.github.com/repos/facebook/react/languages

Quick response

We are now ready to mock the API response from GitHub. To this end, we will use getOnce, which limits the GET mock to being called one time only. Note that before calling getOnce, we call the restore method, which

restores fetch() to its unstubbed state and clears all data recorded for its calls.

We need to call restore to isolate the mocks to the story they are intended for, otherwise a mock declared in a story could be consumed by another one. For our first success case we are simply going to return the payload, a shorthand for returning a response object with status 200 and the payload as the body.

We are now fully independent from the network! We can start working on the cases we previously overlooked. We will write the stories first, then implement the desired behaviour in the component, similarly to Test Driven Development.

Erroneous response

We add the story for when we receive a 403 status code from GitHub, as we have hit the rate limit for non authenticated calls.

Slow response

Brilliant, we can now handle the erroneous response from GitHub. One last thing is missing: mocking the network delay. This can be achieved very easily in fetch-mock, passing a Promise as the response argument:

My two cents

Combining react-storybook with fetch-mock allowed me to quickly sketch user’s flows where network responses affect user experience. This surely allowed me to improve my productivity, and I hope it might help or at least inspire you.

Show me the code

Code for this article can be found here.


Post Scriptum

In this specific example, the function fetching the data and returning a promise should be passed via props, or componentDidMount would just dispatch the proper reduxish action and the data itself would be passed as a prop.

Like what you read? Give Edoardo Colombo a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.