Testing components that depend on Context using React Testing Library or Jest
Asked Answered
H

2

6

All of my components get data and methods from the Context. I want to test -at least- "component renders" case but of course, when I try to render component in a test case, it cannot find methods/functions and data that come from the Context. I couldn't find a way to pass Context values to a test case using React Testing Library or Jest (I need to use these libraries, no enzyme)

Let's say I want to test a simple Filter component.

Context (primitive usage I guess)

import { useState, useEffect, createContext } from "react";

const Context = createContext();

const ContextProvider = ({ children }) => {
  const [books, setBooks] = useState([]);
  // other data and method code..

  const handleFilter = (value) => {
    const filteredBooks = [...books];
    if (books.length > 0) {
      switch (value) {
        case "alphabetical":
          filteredBooks.sort((a, b) => a.title - b.title);
          setBooks(filteredBooks);
          break;
        case "publishdate":
          filteredBooks.sort(
            (a, b) => b.first_publish_year - a.first_publish_year
          );
          setBooks(filteredBooks);
          break;
        default:
          setBooks(filteredBooks);
      }
    }
  };

  return (
    <Context.Provider value={{ handleFilter }}>
      {children}
    </Context.Provider>
  );
};

export { ContextProvider, Context };

Filter

import { useContext } from "react";
import { Context } from "../context/Context";

function Filter() {
  const { handleFilter } = useContext(Context);

  return (
    <div className="filter" data-testid="filter-test">
      <div className="filter-content">
        <select
          defaultValue="none"
          className="filter-btn"
          onChange={(e) => handleFilter(e.target.value)}
        >
          <option value="none" defaultValue disabled>
            Filter Results
          </option>
          <option value="alphabetical">Alphabetically</option>
          <option value="publishdate">Publish Date (newest)</option>
        </select>
      </div>
    </div>
  );
}

export default Filter;

Filter.test.js

import { render, screen, cleanup } from '@testing-library/react';
import Filter from './Filter';

test('renders Filter component without crashing', () => {
  // I need to pass handleFilter function using Context somehow
  render(<Filter />);
  const filterComponent = screen.getByTestId('filter-test');
  expect(filterComponent).toBeInTheDocument();
});

If you want to see all code: https://codesandbox.io/s/inspiring-bhaskara-0s98g

Hypaethral answered 18/5, 2021 at 17:59 Comment(1)
Good article: samdawson.dev/article/react-context-testingMisappropriate
B
8

The simple way of doing this is by passing your context provider as a wrapper of the first argument of render():

import { render, screen, cleanup } from '@testing-library/react';
import Filter from './Filter';

// import your context; it will include the provider
import { Context } from "../context/Context";

test('renders Filter component without crashing', () => {
  const contextValue = { handleFilter: () => {} };
  render(
    <Context.Provider value={contextValue}>
      <Filter />
    </Context.Provider>
  );

  const filterComponent = screen.getByTestId('filter-test');
  expect(filterComponent).toBeInTheDocument();
});

The other way is to provide your context as a wrapper to the render:

import { render, screen, cleanup } from '@testing-library/react';
import Filter from './Filter';

// import your context; it will include the provider
import { Context } from "../context/Context";

test('renders Filter component without crashing', () => {
  const contextValue = { handleFilter: () => {} };
  const wrapper = ({ children }) => (
    <Context.Provider value={contextValue}>
      {children}
    </Context.Provider>
  );

  render(<Filter />, { wrapper });

  const filterComponent = screen.getByTestId('filter-test');
  expect(filterComponent).toBeInTheDocument();
});

Either of the above two ways should work

Bratislava answered 18/5, 2021 at 18:27 Comment(2)
Which one is better do you think?Hypaethral
I prefer the first one, it's a bit more directBratislava
M
0

The easiest way is to add an object as second parameter of the render method. You assign your Context Provider component as the value if that object wrapper property like this :

render(<MyComponent />,{ wrapper: ContextProvider})

Here is a blog post that explain well how to that correctly : How to test React Components depending on the Context API

Migrate answered 11/1, 2023 at 22:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.