Mocking React context provider in Jest with react-testing-library

13,533

The error means that AuthContext.Provider is not React component. AuthContext is Jest spy, i.e. a function, and it doesn't have Provider property. Since AuthContext is expected to be React context, it should be defined as such. For default export it should be:

jest.mock("../contexts/AuthContext", () => ({
  __esModule: true,
  default: React.createContext()
}));

Then any mocked value can be provided in tests as:

<AuthContext.Provider value={mockValue}>
Share:
13,533
CrustyRatFink
Author by

CrustyRatFink

Productivity over perfection. Get it done first and then optimize if necessary. Biggest Professional Challenge: Developer, manager, or entrepreneur? (Hint: yes) Biggest Technical Challenge: Rails or Elixir? Elixir or Scala? Scala or React? UI or Infrastructure? Focus, man, focus! Biggest Personal Challenge: Accept change of all kinds and don't get upset about it. Remember, the sun's going to explode eventually.

Updated on June 04, 2022

Comments

  • CrustyRatFink
    CrustyRatFink almost 2 years

    I have a fairly complex context that I wrap around my app to handle authentication and provide the associated data retrieved from the auth service. I'd like to bypass all of the functionality of the provider and just mock a return value. When the context renders, it performs a bunch of initialization functions that I don't want to happen when I test.

    I tried something like this in my wrapper function:

    const mockValue = {
      error: null,
      isAuthenticated: true,
      currentUser: 'phony',
      login: jest.fn(),
      logout: jest.fn(),
      getAccessToken: jest.fn(),
    }
    
    const MockAuthContext = () => ( React.createContext(mockValue) )
    
    jest.mock("../contexts/AuthContext", () => ({
      __esModule: true,
      namedExport: jest.fn(),
      default: jest.fn(),
    }));
    
    beforeAll(() => {
      AuthContext.mockImplementation(MockAuthContext);
    });
    
    const customRender = (ui, { ...renderOpts } = {}) => {
      const ProviderWrapper = ({ children }) => (
          <AuthContext.Provider>
             {children}
          </AuthContext.Provider>
      );
      return render(ui, { wrapper: ProviderWrapper, ...renderOpts });
    };
    
    // re-export everything
    export * from "@testing-library/react";
    
    // override render method
    export { customRender as render };
    

    Alas, I get an error: Error: Uncaught [Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

    And, indeed, if I log what that Provider renders as, I get:

        {
          '$$typeof': Symbol(react.element),
          type: undefined,
          ...
    

    I've tried setting the provider to AuthContext.getMockImplementation().Provider. No dice.

    Anyway, does anyone see what I'm trying to accomplish here? I'd like to just mock the entire context so that the component just gets a provider that returns known values without executing any context code. Is that possible with react-testing-library and Jest?

  • CrustyRatFink
    CrustyRatFink over 3 years
    Ah, much less hacky. Thanks, I'm set, but I'll give that a whirl later for educational purposes. Thanks!