Create and Test a Hook in React-Native
October 13, 2019 • 2 minutes to read
In React 16.8 Hook was introduced. This was kind of mind-blowing for me. First time I did see those I was like ok
this is just some kind of syntax sugar etc. But after playing with Hooks for like months I'm loving this. The first thing they makes is to let sharing code easier.
In the past Class
component was great, but did you try to share some code with that? It's not easy. For me hook even give me a lot of love back to React.
So why this post? One thing I did find online is how to create Hook. Perfect great, but how can I test them? Also, one thing I find missing in React-Native's tutorial is testing. I have a youtube channel and like others React-Native developer on this platform "even Udemy etc" we don't show testing. It's normal too, cause testing take lot of time. So when doing example like me a big tutorial series of already 40+ videos imagine adding testing.
So here I will try to show you how you can start doing a bit more test in React-Native and also how to do this with Hook.
P.S I'm not saying I'm an expert here, just want to help you to get more into testing.
First thing, a library that will make our lives easier is react-hooks-testing-library. This will help us a lot in this process. One reason is the fact then this will help us testing Hook without mounting components. So the unit testing of the Hook will be painless.
useStatusBar
For the first demo, we will make a simple Hook who will set the statusBar color when the screen is focused. For this we will also use the library react-navigation-hooks
This use typescript
but can use plain javascript
just remove the types :)
1import { useEffect } from 'react';2import { StatusBar, StatusBarStyle } from 'react-native';3import { useFocusState } from 'react-navigation-hooks';45const useStatusBar = (style: StatusBarStyle, animated = true) => {6 const { isFocused } = useFocusState();78 useEffect(() => {9 if (isFocused) {10 StatusBar.setBarStyle(style, animated);11 }12 }, [isFocused]);13};1415export default useStatusBar;
Here as you can see it's a pretty simple case. The Hook will run a effect when the isFocused
boolean value will change.
This will happen when the sceen get focused.
Now how can we test that?
The first thing we will need is to create a file call useStatusBar.test.ts
Inside this one we will need to render the hook first
1import { renderHook } from '@testing-library/react-hooks';23import useStatusBar from '../useStatusBar';45describe('useStatusBar', () => {6 it('should set the status bar style as light-content when screen is focused', () => {7 renderHook(() => useStatusBar('light-content'))8 });9});
The hook first argument is the style we do want. Here, in this case, we want light-content
the second argument is a default animated value to true.
As you can see render the Hook is simple. Time to add some testing. The first thing we need to
do for accomplishing the Unit Test is to mock dependencies. In this one we have 2 deps to mock. The StatusBar native API and the react-navigation hook.
How can we do this? Jest to the rescue!!!
Jest came with Spy + Mock and this is all we need. So first time to spy on the StatusBar API.
1import { StatusBar } from 'react-native';2import { renderHook } from '@testing-library/react-hooks';34import useStatusBar from '../useStatusBar';56describe('useStatusBar', () => {7 it('should set the status bar style as light-content when screen is focused', () => {8 const setBarStyleSpy = jest.spyOn(StatusBar, 'setBarStyle');910 renderHook(() => useStatusBar('light-content'))11 });12});
As you can see, we create a variable call setBarStyleSpy
who will be a spy on the method from the StatusBar setBarStyle
. We
will spy on this one so we can make sure we do call this method once with the value we expected.
1import { StatusBar } from 'react-native';2import { renderHook } from '@testing-library/react-hooks';34import useStatusBar from '../useStatusBar';56describe('useStatusBar', () => {7 it('should set the status bar style as light-content when screen is focused', () => {8 const setBarStyleSpy = jest.spyOn(StatusBar, 'setBarStyle');910 renderHook(() => useStatusBar('light-content'))1112 expect(setBarStyleSpy).toHaveBeenCalledTimes(1);13 expect(setBarStyleSpy).toHaveBeenCalledWith('light-content', true);14 });15});
Here we do check setBarStyle
did get called only once and the value provided was light-content
and true
who is the default value pass.
In the last step we do need to mock the react-navigation-hooks
.
1import { StatusBar } from 'react-native';2import { renderHook } from '@testing-library/react-hooks';34import useStatusBar from '../useStatusBar';56describe('useStatusBar', () => {7 it('should set the status bar style as light-content when screen is focused', () => {8 const setBarStyleSpy = jest.spyOn(StatusBar, 'setBarStyle');910 jest11 .spyOn(reactNavigationHooks, 'useFocusState')12 .mockImplementation(() => ({ isFocused: true }));1314 renderHook(() => useStatusBar('light-content'))1516 expect(setBarStyleSpy).toHaveBeenCalledTimes(1);17 expect(setBarStyleSpy).toHaveBeenCalledWith('light-content', true);18 });19});
Here we mock only once the useFocusState
method to return an object who contain isFocused: true
this one will trigger
the useEffect of our hook who then will cause the StatusBar setBarStyle
to get a call.
That's it this is how we can use jest and mock/spy on dependencies of our code so we can Unit Test easily.