'Unit testing: react context api with enzyme return an empty object

I am trying for the first time to use React context API to pass information from a main component to a grandchild component.

So first I have created a context

const MyContext = React.createContext({});

export default MyContext;

Here is the main component that sets the value of the context

import MyContext from "./MyContext.js";
import ParentComponent from "./ParentComponent.js";

function App() {
  return (
    <div>
      <MyContext.Provider value={{ foo: 12 }}>
        <ParentComponent />
      </MyContext.Provider>
    </div>
  );
}

The parent component doesn't care about the context and is just here to create the grandchild component

import ChildComponent from "./ChildComponent.js";

class ParentComponent extends Component {
  render() {
    return (
      <div>
        Parent
        <ChildComponent />
      </div>
    );
  }
}

export default ParentComponent;

And here is the child component that reads the context

import MyContext from "./MyContext.js";

class ChildComponent extends PureComponent {
  constructor() {
    super();
    this.state = {
      bar: 456
    };
  }

  render() {
    return (
      <div>
        <MyContext.Consumer>
          {({ foo }) => (
            <div>
              <h1>Hello I'm the ChildComponent</h1>
              <h2>Context value: {foo}</h2>
              <h2>State value: {this.state.bar}</h2>
              <button onClick={() => this.setState({ bar: foo })}>
                Click me
              </button>
            </div>
          )}
        </MyContext.Consumer>
      </div>
    );
  }
}

export default ChildComponent;

So far no problem. Everything works as expected. The ChildComponent has retrieved the context value.

The problem comes when I try to test it with jest/enzyme. I can't manage to set the context

it("Should test the context value", () => {
  let wrapper = mount(<ChildComponent />, {
    context: { foo: 987 }
  });

  expect(wrapper.state("bar")).toEqual(456);
  expect(wrapper.context("foo")).toEqual(987);
});

The last expect fails and the context value is an empty object. So foo is undefined

I have recreated the problem here: https://codesandbox.io/embed/x25yop4x5w?fontsize=14

Thank you for your help



Solution 1:[1]

Enzyme context affects legacy React context, not modern context API. It should be mocked with:

mount(<MyContext.Provider value={{foo: 987}}><ChildComponent/></MyContext.Provider>)

And asserted with:

expect(wrapper.find('h2').text()).toBe('Context value: 987');

Solution 2:[2]

The method I've used to test components which require being mounted in within a context provider tree is to use the wrappingComponent and wrappingComponentProps options for mount.

This ensures that the return value of mount (the root component) is still the component you want to test (and will still support APIs/options that only work on the root component, such as setProps).

mount(<MyComponent />, {
  wrappingComponent: MyContext.Provider,
  wrappingComponentProps: {
    value: { foo: 987 },
  },
})

Solution 3:[3]

I had a similar problem. Using shallow did not work but then found this dependency. After installing and implementing it, it did work.

https://www.npmjs.com/package/shallow-with-context

What you can do is

import {withContext} from 'shallow-with-context';

...

test("ChildComponent", () => {
  const context = { foo: 987 };
  const ChildComponentWithContext = withContext(<ChildComponent />, context);

  it("Should test the state setup", () => {
    let wrapper = shallow(<ChildComponentWithContext />, { context });

    expect(wrapper.state().bar).toEqual(456);
  });

  it("Should test the context value", () => {
    let wrapper = shallow(<ChildComponentWithContext />, { context });

    expect(wrapper.state().bar).toEqual(456);
    expect(wrapper.context().foo).toEqual(987);
  });
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Estus Flask
Solution 2 jamis0n
Solution 3 Yusuf Alp