Visual unit testing with react-storybook and fetch-mock
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:
- A successful and quick response (less than 200ms).
- A successful and slow response (more than 200ms).
- 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.
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.
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?
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.
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, among other things, will:
- create the
src/storiesfolder, where our stories will be loaded from;
- add the
"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"
getstorybook is done, we can start
storybook by running:
→ npm run storybook
If we visit localhost:9009, we shall see the welcome page:
We can now modify
.storybook/config.js to automatically load as stories all the
.stories.js files in
Remove everything in
src/stories, and add
Stop and start again
npm run storybook and behold the
App component loaded in our 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.
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
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.
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.
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
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.
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.