Component that leverages theme + withStyles with custom variable doesn't fail. I have a component that I wrap with withStyles
. I only wrap the top level index with withRoot
(where the theme is added to context). How to easily use shallow
, mount
, etc. to test a sub-component? I.e. how do I get the theme into the context? I feel like this should be an easy fix, but I can't find a good example for this. See the test results in the codesandbox link.
Test fails
See test results in https://codesandbox.io/s/wwkjkxzxj8
withRoot
I added a custom theme variable success.main
:const theme = createMuiTheme({
palette: {
primary: {
light: purple[300],
main: purple[500],
dark: purple[700]
},
secondary: {
light: green[300],
main: green[500],
dark: green[700]
}
},
success: {
main: "pink"
}
});
Component
which uses the theme variable:import React from "react";
import { withStyles } from "@material-ui/core/styles";
const styles = theme => ({
customClass: {
color: theme.success.main
}
});
class Component extends React.Component {
render() {
const { classes } = this.props;
return <div className={classes.customClass}>Dude</div>;
}
}
export default withStyles(styles)(Component);
withRoot
at the top level, if you run the tests, they barf because theme.success
is undefined Cannot read property 'main' of undefined
TypeError: Cannot read property 'main' of undefined
at styles (https://ymv6ow7021.codesandbox.io/src/components/Component.js:26:28)
at Object.create (https://ymv6ow7021.codesandbox.io/node_modules/@material-ui/core/styles/getStylesCreator.js:26:35)
at WithStyles.attach (https://ymv6ow7021.codesandbox.io/node_modules/@material-ui/core/styles/withStyles.js:269:45)
at new WithStyles (https://ymv6ow7021.codesandbox.io/node_modules/@material-ui/core/styles/withStyles.js:143:15)
at ReactShallowRenderer.render (https://ymv6ow7021.codesandbox.io/node_modules/react-test-renderer/cjs/react-test-renderer-shallow.development.js:131:26)
at eval (https://ymv6ow7021.codesandbox.io/node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:287:35)
at withSetStateAllowed (https://ymv6ow7021.codesandbox.io/node_modules/enzyme-adapter-utils/build/Utils.js:94:16)
at Object.render (https://ymv6ow7021.codesandbox.io/node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:286:68)
at new ShallowWrapper (https://ymv6ow7021.codesandbox.io/node_modules/enzyme/build/ShallowWrapper.js:119:22)
at shallow (https://ymv6ow7021.codesandbox.io/node_modules/enzyme/build/shallow.js:19:10)
at shallowWithContext (https://ymv6ow7021.codesandbox.io/node_modules/@material-ui/core/test-utils/createShallow.js:35:19)
at Object.eval (https://ymv6ow7021.codesandbox.io/src/components/Component.test.js:29:19)
Easily test sub components without wrapping in too many HOC.
I used codesandbox provided link with latest create-react-app, enzyme, etc.
+1 - having the exact same issue.
I think that it would be a good opportunity to add such in the documentation:
Component.js
import React from "react";
import { withStyles } from "@material-ui/core/styles";
const styles = theme => ({
root: {
color: theme.success.main
}
});
class Component extends React.Component {
render() {
const { classes } = this.props;
return <div className={classes.root}>Dude</div>;
}
}
export default withStyles(styles)(Component);
Component.test.js
import React from "react";
import { shallow, mount } from "enzyme";
import { unwrap } from "@material-ui/core/test-utils";
import MuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider";
import Component from "./Component";
const ComponentNaked = unwrap(Component);
describe("<Component />", () => {
it("with shallow", () => {
const wrapper = shallow(<ComponentNaked classes={{}} />);
console.log("shallow", wrapper.debug());
});
it("with mount", () => {
const wrapper = mount(
<MuiThemeProvider
theme={{
success: {
main: "#fff"
}
}}
>
<Component />
</MuiThemeProvider>
);
console.log("mount", wrapper.debug());
});
});
@oliviertassinari unwrap()
seems to be missing from the type definitions (test-utils/index.d.ts
)?
unwrap() seems to be missing from the type definitions
@kallebornemark Right, we need to add it 馃憤
@kallebornemark Care to give it a go?
Have you tried using dive()
? http://airbnb.io/enzyme/docs/api/ShallowWrapper/dive.html
MUI also has some test utils to help: https://material-ui.com/guides/testing/#api
They wrap enzyme's shallow
with some helpful options like untilSelector
: "Recursively shallow renders the children until it can find the provided selector. It's useful to drill down higher-order components.". There's examples in the actual codebase. But I think you would use it like so:
E.g.
import * as React from 'react';
import {createShallow} from '@material-ui/core/test-utils';
import Foo from './Foo';
describe('<Foo />', () => {
let shallow;
beforeEach(() => {
shallow = createShallow({untilSelector: 'Foo'});
});
it('should render', () => {
const wrapper = shallow(<Foo />);
console.log(wrapper.debug());
});
});
@mynameistechno Thank you very much for the speedy reply. I somehow accidentally deleted my question. For anyone wondering in the future, my question was:
How to test components wrapped by withRoot()
as shown in the tutorials.
withRoot()
function sketch:
function withRoot(Component) {
function WithRoot(props) {
return (
<MuiThemeProvider theme={theme}>
<CssBaseline />
<Component {...props} />
</MuiThemeProvider>
);
}
return WithRoot;
}
App.js
sketch:
function App() {
return (
<Foo>
<div>
<Bar />
<Bar />
</div>
</Foo>
);
}
export default withRoot(App);
For your response, I tried to use dive()
but couldn't manage to make it work. I can't seem to find the App component when diving into MuiThemeProvider.
As for the untilSelector
you suggested, I always get this error:
Warning: React.createElement: 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.
I tried both:
const shallow = createShallow({ untilSelector: 'App' });
const shallow = createShallow({ untilSelector: App });
and then shallow rendered, they all gave me the error. It might be some version imcompatible issue.
But after some more research, thanks to your guide, I found a working solution for my current setup.
describe('App shallow tests', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<App />).find('App').shallow();
});
it('contains a Foo', () => {
expect(wrapper.find(Foo).length).toBe(1);
});
});
Thank you again and I hope this would help others.
You CAN manually provide the context with the custom theme & custom variables to withRoot
, withStyles
etc (I was having the exact same problem with withStyles
). See my comment here for how I provide that context to shallow
.
@issuehuntfest has funded $40.00 to this issue. See it on IssueHunt
I had more or less the same problem, but in my case I was using withTheme
, instead of withStyles
.
I've adapted your codesandbox here with the test passing.
I'm only using react-testing-library
, but the solution should work with your current test set up.
Here is the test updated:
import React from "react";
import { render, waitForElement, cleanup } from "react-testing-library";
import { StyledComponent } from "./Component";
afterEach(cleanup);
describe("Test SignIn", () => {
test("it renders sign in text", async () => {
const { getByText } = render(<StyledComponent />);
await waitForElement(() => getByText(/Dude/i));
});
});
I had to do a named export for this to work without passing the styles:
export const StyledComponent = withStyles()(Component);
export default withStyles(styles)(Component);
I've tried to fix this following the material-ui testing documentation and @oliviertassinari response, but I was not able to make it work.
This is more of a general question regarding testing react components. If you use a custom context then you are responsible for either mocking it or ensuring your components are only rendered in the actual context.
I guess most of the issues arise from using shallow rendering API which we no longer encourage. We had to refactor way too many test without anything actually breaking because of heavy usage of shallow rendering. We now use almost exclusively mount
with aria selectors internally and keep usage of #instance
or find(ComponentType)
to a minimum.
I guess most of the issues arise from using shallow rendering API which we no longer encourage.
Agreed. I've also started to migrate away from shallow rendering with enzyme and using dom-testing-library instead. Additionally we use a render wrapper function for rendering in tests that includes all the context we need. Here is a simplified version built on examples from dom-testing-library that add our redux store and mui theme:
// test-utils.js
import {render} from 'react-testing-library';
import theme from '../ourCustomTheme';
export function renderWithStore(element, { initialState, store = createStore(reducer, initialState) } = {}){
return {
...render(
<Provider store={store}>
<MuiThemeProvider theme={theme}>{element}</MuiThemeProvider>
</Provider>
),
store
};
}
I think we can close this ticket.
:+1: for closing, unless @eps1lon wants to update the documentation.
Most helpful comment
I think that it would be a good opportunity to add such in the documentation:
Component.js
Component.test.js