5

I have one component with Redux useSelector, when I want to test the render text, it shows error message on useSelector and with the error message: could not find react-redux context value; please ensure the component is wrapped in a <Provider>. I have put <Provider> to the top level component, where is the wrong part on my testing code? The app itself is running correct.

TopLevel component:

import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
import { Provider } from "react-redux"
import store from "./redux/store"

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById("test")
)

App.js

import { useEffect, useState, useCallback } from "react"
import { useDispatch, useSelector } from "react-redux"
import { fetchPosts, showName } from "./redux/actions/posts"
import Main from "./component/main"

function App() {
    const { posts, name } = useSelector((state) => state.posts)
    const dispatch = useDispatch()

    const handleClick = useCallback(() => {
        dispatch(fetchPosts())
    }, [])

    const handleShowName = useCallback((textName) => {
        dispatch(showName(textName))
    }, [])

    return (
        <div className="App" data-test="appComponent">
            <button onClick={handleClick}>get post</button>
            <button onClick={() => handleShowName("test")}>Show name</button>
            <section className="main">
                <div>project name: {name}</div>
                <Main posts={posts} />
            </section>
        </div>
    )
}

export default App

app.test.js

import React from "react"
import App from "./App"
import { render, fireEvent, screen } from "@testing-library/react"

it("render a div with text 'project name'", () => {
    const { container, getByText } = render(<App />)
    expect(getByText("project name:")).toBeInTheDocument()
})

When I test my redux action and redux reducer, my testing is working. So the Jest setting should be fine.

jest.config.js

module.exports = {
    setupFilesAfterEnv: ["./setupTests.js"]
}

setupTest.js

import '@testing-library/jest-dom/extend-expect';

babel.config.js

module.exports = {
    presets: ["@babel/preset-env", "@babel/preset-react"],
    plugins: [
        ["@babel/plugin-proposal-class-properties", { loose: false }],
        [
            "@babel/plugin-transform-runtime",
            {
                corejs: 3,
                helpers: true,
                regenerator: true
            }
        ]
    ]
}

1 Answer 1

7

You need to provide a redux context for the component being tested (in isolation).

Minimum is to create a simple wrapper component (see also custom render), similar to the provider in the app, passed in the options of the render test utility.

import { Provider } from "react-redux"
import store from "./redux/store"

const ReduxWrapper = ({ children }) => (
  <Provider store={store}>
    {children}
  </Provider>
);

it("render a div with text 'project name'", () => {
  const { container, getByText } = render(
    <App />,
    { wrapper: ReduxWrapper },
  );
  expect(getByText("project name:")).toBeInTheDocument()
});

See the Redux docs for testing connected components.

The gist is to create a custom render function that includes your redux store.

import React from 'react';
import { render as rtlRender } from '@testing-library/react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
// Import your own reducer
import store from "./redux/store";

function render(
  ui,
  {
    initialState,
    store = createStore(reducer, initialState),
    ...renderOptions
  } = {}
) {
  function Wrapper({ children }) {
    return <Provider store={store}>{children}</Provider>
  }
  return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
}

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

Then for testing you import the custom render utility

import { render, fireEvent, screen } from "../testUtils";

it("render a div with text 'project name'", () => {
  const { container, getByText } = render(
    <App />,
    // pass any options here like initial state, custom store, etc...
  );
  expect(getByText("project name:")).toBeInTheDocument();
});
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.