Tengo un componente Todo simple que utiliza ganchos de reacción-redux que estoy probando con enzimas, pero recibo un error o un objeto vacío con una representación poco profunda, como se indica a continuación.
¿Cuál es la forma correcta de probar componentes usando ganchos de 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); });error v1:
... 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); }); Las pruebas v2 también fallan ya que el wrapper es el <Provider> y llamar a dive() en el wrapper devolverá el mismo error que v1.
¡Gracias de antemano por tu ayuda!
Para simular useSelector use puede hacer esto
import * as redux from 'react-redux' const spy = jest.spyOn(redux, 'useSelector') spy.mockReturnValue({ username:'test' })Podría probar un componente que usa ganchos redux usando la instalación de montaje de enzimas y proporcionando una tienda simulada al Proveedor:
Componente
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, ie 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;Prueba
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) }) })Hay otra forma que @abidibo si usa un selector de función definido en otro archivo. Puede useSelector y su función de selector, y luego usar la enzima shallow :
Componente
import * as React from 'react'; import { useSelector } from 'react-redux'; import Spinner from './Spinner'; import Button from './Button '; import { getIsSpinnerDisplayed } from './selectors'; const Example = () => { const isSpinnerDisplayed = useSelector(getIsSpinnerDisplayed); return isSpinnerDisplayed ? <Spinner /> : <Button />; }; export default Example;Selectores
export const getIsSpinnerDisplayed = state => state.isSpinnerDisplayed;Prueba
import * as React from 'react'; import { shallow } from 'enzyme'; import Example from './Example'; import Button from './Button '; import { getIsSpinnerDisplayed } from './selectors'; jest.mock('react-redux', () => ({ useSelector: jest.fn(fn => fn()), })); jest.mock('./selectors'); describe('Example', () => { it('should render Button if getIsSpinnerDisplayed returns false', () => { getIsSpinnerDisplayed.mockReturnValue(false); const wrapper = shallow(<Example />); expect(wrapper.find(Button).exists()).toBe(true); }); });Puede ser un poco complicado, pero funciona bien para mí :)