Material-ui: Shallow testing (enzyme) component with custom theme and mui-styles' makeStyles hook

Created on 18 Apr 2019  ·  9Comments  ·  Source: mui-org/material-ui

Can you add a way (or add documentation) to shallow test a component with a custom theme and using styling hooks?
Using unwrap worked with withStyles to shallow mount a component, but now with hook styling there is no longer a HOC styling wrapper. Dive is also not working as expected.

ex.

const theme = createTheme(/*custom theme here */);

const useStyles = makeStyles(theme => ({
    customStyle: {
          color: /* custom theme value here */
}));

const MyComponent = () => {

    const classes = useStyles();

    return (
        <div className={classes.customStyle} />
    );
}

I'm use shallow rendering (enzyme), and I wrap this component in the themeprovider from mui-styles. Diving into the themeprovider I get an error. Can you either add documentation surrounding this or add a clear way to test styling hooks?

duplicate

Most helpful comment

@BenBrewerBowman I was running into the same issues and I've found a way around using jest mocks. Here are the steps for my workaround:
1) Create a manual mock at the root of your project (defined in jest.config). The path should look like <root>/__mocks__/@material-ui/styles.ts
2) Add the following code into the file:

// Grab the original exports
import * as Styles from "@material-ui/styles";

const makeStyles = () => {
    return () => {
        /**
         * Note: if you want to mock this return value to be
         * different within a test suite then use
         * the pattern defined here:
         * https://jestjs.io/docs/en/manual-mocks
         **/
        return {};
    };
};

module.exports = { ...Styles, makeStyles };

You should now be able to use shallow rendering on all your tests. Just a couple of notes:
1) I'm using typescript but you should be able to easily adapt this for JS
2) Be sure that your mock file path matches what you are importing. In my case our imports always look like this: import { makeStyles } from "@material-ui/styles";

edit: updated mock to be much simpler (inspired by: https://github.com/mui-org/material-ui/issues/14357#issuecomment-470324935)

All 9 comments

Could you share your testing setup and the error you get?

const useStyles = makeStyles((theme: any) => ({
    button: {
      // we've added this to the theme 
      color: theme.palette.red[500]
    }
  }));

  const CustomComponent = ({ title }: { title: string}) => {
    const classes = useStyles();
    return (
      <Typography className={classes.button} >
        {title}
      </Typography>
    )
  }

  const title = 'some title';

  const themeWrapper = shallow(
    <ThemeProvider theme={theme}>
      <CustomComponent title={title} />
    </ThemeProvider>
  );

 // I have tried all of the following
 const wrapper = themeWrapper;
 const wrapper = themeWrapper.dive();
 const wrapper = themeWrapper.childAt(0).dive();
 const wrapper = themeWrapper.find(CustomComponent).dive();

  test('displays typography with text', () => {
    expect(wrapper.find('Typography').text()).toEqual(title);
  });

This test fails each time with the error:
Method “text” is meant to be run on 1 node. 0 found instead.

First off am I doing this test correctly. Is there something I'm missing? If not, what is the correct solution to shallow render this example? I'm getting this to pass with mount, but mounting is really driving up our build times.

@BenBrewerBowman I believe we already have a discussion for this problem: #11864.

I would encourage you to use the mount API and to forget about the existence of the shallow one, or even to consider using react-testing-library.
I don't think that enzyme shallow API support the context provider pattern yet, e.g. https://github.com/airbnb/enzyme/issues/2103. React shallow test utils might not support it either: https://github.com/facebook/react/pull/14329.

but mounting is really driving up our build times.

I understand the concern. Did you benchmark it? We are migration the whole Material-UI test suite away from the shallow API. I personally think that the code refactoring flexibility the mount API provides worth a slower test suite. We haven't benchmarked any significant difference yet. The tests on the master branch take 32s, the tests on the next branch, where we started the mount migration take 42s (but we added many new tests).

@BenBrewerBowman I was running into the same issues and I've found a way around using jest mocks. Here are the steps for my workaround:
1) Create a manual mock at the root of your project (defined in jest.config). The path should look like <root>/__mocks__/@material-ui/styles.ts
2) Add the following code into the file:

// Grab the original exports
import * as Styles from "@material-ui/styles";

const makeStyles = () => {
    return () => {
        /**
         * Note: if you want to mock this return value to be
         * different within a test suite then use
         * the pattern defined here:
         * https://jestjs.io/docs/en/manual-mocks
         **/
        return {};
    };
};

module.exports = { ...Styles, makeStyles };

You should now be able to use shallow rendering on all your tests. Just a couple of notes:
1) I'm using typescript but you should be able to easily adapt this for JS
2) Be sure that your mock file path matches what you are importing. In my case our imports always look like this: import { makeStyles } from "@material-ui/styles";

edit: updated mock to be much simpler (inspired by: https://github.com/mui-org/material-ui/issues/14357#issuecomment-470324935)

@mdvorscak Thanks for sharing, it looks similar to: https://github.com/mui-org/material-ui/issues/14357#issuecomment-470324935.

@oliviertassinari You're welcome.

Thanks @oliviertassinari. Maybe this method of mocking makeStyles could be documented somewhere? Note: I also cleaned up my mock code to be as minimal as possible

@mdvorscak Well, we don't encourage shallow testings, it's not something we want people to rely on. It can be frustrating.
In the reference, I have shared, the use case is for snapshot testing. Those snapshot tests should be easier in v4 as we generate global class names. So, I agree, it can be documented 👍.

I also cleaned up my mock code to be as minimal as possible

Your clean up mock wouldn't cut it for the snapshot use case.

I have been using @mdvorscak implementation and placing the mock inside <root>/__mocks__/@material-ui/styles.ts. However, when I upgraded to the new react scripts and jest everything started failing.

I solved that by moving the mock inside src. I wonder if that change is a feature or a bug 😄

Was this page helpful?
0 / 5 - 0 ratings