Testing styled components with react-testing-library

Viewed 9438

I'm currently trying to test a styled component with a Mocked Provider as follows:

import React from "react";
import TestResults from "./TestResults";
import {
  render,
  cleanup,
  findByTestId,
  findByText,
  waitForElement,
} from "@testing-library/react";
import { MockedProvider } from "@apollo/react-testing";




describe("TestResultsComponent", () => {
  describe("Overall", () => {
    it("should render successfully - base", async () => {
      const { getByText } = render(
        <MockedProvider>
          <TestResults />
        </MockedProvider>
      );
      expect(getByText("Preview")).toBeInTheDocument();
    });
  });
});

I'm using the makeStyles hook in the TestResults file

When I run my tests, I receive the following error:

TypeError: theme.spacing is not a function
 Material-UI: the `styles` argument provided is invalid.
      You are providing a function without a theme in the context.
      One of the parent elements needs to use a ThemeProvider.

I'm not sure if I should mock out the implementation of makeStyles. This is the my first time seeing an error like this, I have been testing other components that use the same hook and it has not been an issue.
2 Answers

@material-ui/styles styling solution is standalone, it doesn't know anything about Material-UI components. You need to use the ThemeProvider from the style-components package with the const theme = createMuiTheme() function from the core package and render this with your test. Best case would be that you have defined your theme already somewhere in your application and simply can import it.

so your test should become:

import React from "react";
import {ThemeProvider} from 'styled-components';
import { createMuiTheme } from '@material-ui/core/styles';
import TestResults from "./TestResults";
import {
  render,
  cleanup,
  findByTestId,
  findByText,
  waitForElement,
} from "@testing-library/react";
import { MockedProvider } from "@apollo/react-testing";

describe("TestResultsComponent", () => {
  describe("Overall", () => {
    it("should render successfully - base", async () => {
      const theme = createMuiTheme()
      const { getByText } = render(
        <ThemeProvider theme={muiTheme}>
          <MockedProvider>
            <TestResults />
          </MockedProvider>
        </ThemeProvider>
      );
      expect(getByText("Preview")).toBeInTheDocument();
    });
  });
})

If this boilerplate for wrapping your components becomes to much you can also write a Wrapper-Component which sets up your needed components and pass this component as second argument to the render-Options

See the docs for the wrapper here

Thank to @takethefake answer, in my case we didn't use MaterialUi but it was quite similar:

import React from 'react';
import { render, screen } from '@testing-library/react';
import { ThemeProvider } from 'styled-components';
import { STRING } from '../constants';
import Theme from '../styles/theme';
import { PreOnboarding } from './PreOnboarding';

test('la valeur `trigger.cta` est utilisée comme fallback du titre', () => {
  const trigger = { cta: 'fallback title', text: STRING.empty };
  const theme = Theme({ color1: 'red', color2: 'blue' });

  render(
    <ThemeProvider theme={theme}>
      <PreOnboarding trigger={trigger} />
    </ThemeProvider>
  );
  const button = screen.getByRole('label', { name: /fallback title/i });
  expect(button).toBeVisible();
});
Related