I have a simple Todo component that utilizes react-redux hooks that I'm testing using enzyme but I'm getting either an error or an empty object with a shallow render as noted below.
What is the correct way to test components using hooks from react-redux?
Todos.js
const Todos = () => { const { todos } = useSelector(state => state); return ( <ul> {todos.map(todo => ( <li key={todo.id}>{todo.title}</li> ))} </ul> ); };
Todos.test.js v1
... it('renders without crashing', () => { const wrapper = shallow(<Todos />); expect(wrapper).toMatchSnapshot(); }); it('should render a ul', () => { const wrapper = shallow(<Todos />); expect(wrapper.find('ul').length).toBe(1); });
v1 Error:
... Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a <Provider> ...
Todos.test.js v2
... // imported Provider from react-redux it('renders without crashing', () => { const wrapper = shallow( <Provider store={store}> <Todos /> </Provider>, ); expect(wrapper).toMatchSnapshot(); }); it('should render a ul', () => { const wrapper = shallow(<Provider store={store}><Todos /></Provider>); expect(wrapper.find('ul').length).toBe(1); });
v2 tests also fail since wrapper
is the <Provider>
and calling dive()
on wrapper
will return the same error as v1.
Thanks in advance for your help!
Redux can be tested with any test runner, since it's just plain JavaScript. One common option is Jest, a widely used test runner that comes with Create-React-App, and is used by the Redux library repos. If you're using Vite to build your project, you may be using Vitest as your test runner.
If you need to test a custom Hook, you can do so by creating a component in your test, and using your Hook from it. Then you can test the component you wrote. To reduce the boilerplate, we recommend using React Testing Library which is designed to encourage writing tests that use your components as the end users do.
To mock useSelector use can do this
import * as redux from 'react-redux' const spy = jest.spyOn(redux, 'useSelector') spy.mockReturnValue({ username:'test' })
I could test a component which uses redux hooks using enzyme mount facility and providing a mocked store to the Provider:
Component
import React from 'react'; import AppRouter from './Router' import { useDispatch, useSelector } from 'react-redux' import StartupActions from './Redux/Startup' import Startup from './Components/Startup' import './App.css'; // This is the main component, it includes the router which manages // routing to different views. // This is also the right place to declare components which should be // displayed everywhere, i.e. sockets, services,... function App () { const dispatch = useDispatch() const startupComplete = useSelector(state => state.startup.complete) if (!startupComplete) { setTimeout(() => dispatch(StartupActions.startup()), 1000) } return ( <div className="app"> {startupComplete ? <AppRouter /> : <Startup />} </div> ); } export default App;
Test
import React from 'react'; import {Provider} from 'react-redux' import { mount, shallow } from 'enzyme' import configureMockStore from 'redux-mock-store' import thunk from 'redux-thunk'; import App from '../App'; const mockStore = configureMockStore([thunk]); describe('App', () => { it('should render a startup component if startup is not complete', () => { const store = mockStore({ startup: { complete: false } }); const wrapper = mount( <Provider store={store}> <App /> </Provider> ) expect(wrapper.find('Startup').length).toEqual(1) }) })
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With