29

I got a custom hook which I want to test. It receives a redux store dispatch function and returns a function. In order to get the result I'm trying to do:

const { result } = renderHook(() => { useSaveAuthenticationDataToStorages(useDispatch())});

However, I get an error:

Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a

It happens because of the useDispatch and that there is no store connected. However, I don't have any component here to wrap with a provider.. I just need to test the hook which is simply saving data to a store.

How can I fix it?

CodeMonkey
  • 11,196
  • 30
  • 112
  • 203

4 Answers4

32

The react hooks testing library docs go more into depth on this. However, what we essentially are missing is the provider which we can obtain by creating a wrapper. First we declare a component which will be our provider:

import { Provider } from 'react-redux'

const ReduxProvider = ({ children, reduxStore }) => (
  <Provider store={reduxStore}>{children}</Provider>
)

then in our test we call

test("...", () => {
  const store = configureStore();
  const wrapper = ({ children }) => (
    <ReduxProvider reduxStore={store}>{children}</ReduxProvider>
  );
  const { result } = renderHook(() => {
    useSaveAuthenticationDataToStorages(useDispatch());
  }, { wrapper });
  // ... Rest of the logic
});
Jose Felix
  • 970
  • 5
  • 7
  • Note that if you put this `ReduxProvider` in it's own file (as I did), you need to import react for it to work `import React from 'react';` – fullStackChris Oct 12 '21 at 11:01
  • 7
    what is `configureStore()` ? – Giovanka Bisano Oct 27 '21 at 03:07
  • 1
    [`configureStore()`](https://redux-toolkit.js.org/api/configurestore/) is a helper from redux-toolkit that that adds good defaults to the store setup for a better development experience. `import { configureStore } from "@reduxjs/toolkit";` It's not necessary to use `configureStore` to test custom hooks, though. – Tyler Dane Feb 08 '22 at 18:27
  • PS an empty store didn't work for me based on my custom hook. I had to initialize my store like so to get my test to work `const store = configureStore( reducer: });` – Tyler Dane Feb 08 '22 at 19:24
16

This is probably a late answer but you can also use this in your test

jest.mock('react-redux', () => {
    const ActualReactRedux = jest.requireActual('react-redux');
    return {
        ...ActualReactRedux,
        useSelector: jest.fn().mockImplementation(() => {
            return mockState;
        }),
    };
});
SerzN1
  • 1,814
  • 23
  • 15
hungneox
  • 9,333
  • 12
  • 49
  • 66
10

This issues is related your test file. You have to declarer provider and store in your test file.

Update or replace your app.test.tsx by below code

NB: Don't forget to install redux-mock-store if you don't have already.

import React from 'react';
import { render } from '@testing-library/react';

import App from './App';

import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';

describe('With React Testing Library', () => {
    const initialState = { output: 10 };
    const mockStore = configureStore();
    let store;

    it('Shows "Hello world!"', () => {
        store = mockStore(initialState);
        const { getByText } = render(
            <Provider store={store}>
                <App />
            </Provider>
        );

        expect(getByText('Hello World!')).not.toBeNull();
    });
});

I got this solution after searching 1 hours. Thanks a lot to OSTE

Original Solution: Github issues/8145 and solution link


With this solution if you get error like TypeError: window.matchMedia is not a function then solve by this way. add those line to your setupTests.ts file. Original solution link stackoverflow.com/a/64872224/5404861

global.matchMedia = global.matchMedia || function () {
  return {
    addListener: jest.fn(),
    removeListener: jest.fn(),
  };
};
MD Ashik
  • 9,117
  • 10
  • 52
  • 59
4

I think you can create test-utils.[j|t]s(?x), or whatever you set the name of the file to, like this:

https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart/blob/main/__tests__/test-utils.tsx

//root(or wherever your the file)/test-utils.tsx

import React from 'react';
import { render, RenderOptions } from '@testing-library/react';
import { Provider } from 'react-redux';

// Import your store
import { store } from '@/store';

const Wrapper: React.FC = ({ children }) => <Provider store={store}>{children}</Provider>;

const customRender = (ui: React.ReactElement, options?: Omit<RenderOptions, 'wrapper'>) => render(ui, { wrapper: Wrapper, ...options });

// re-export everything
export * from '@testing-library/react';
// override render method
export { customRender as render };


Use it like this: https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart/blob/main/__tests__/pages/index.test.tsx

//__tests__/pages/index.test.tsx


import React from 'react';
import { render, screen } from '../test-utils';
import Home from '@/pages/index';

describe('Home Pages', () => {
  test('Should be render', () => {
    render(<Home />);
    const getAText = screen.getByTestId('welcome');
    expect(getAText).toBeInTheDocument();
  });
});


Works for me.

screenshot work

BTW, if you place the test-utils.[j|t]s(?x) or whatever you set the name file place on the directory __test__, don't forget to ignore it on jest.config.js.

//jest.config.js

testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/', '<rootDir>/__tests__/test-utils.tsx'],

repo: https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart

ouflak
  • 2,458
  • 10
  • 44
  • 49
Gori
  • 47
  • 3