enzymejs / enzyme

JavaScript Testing utilities for React
https://enzymejs.github.io/enzyme/
MIT License
19.96k stars 2.01k forks source link

Cannot read property from theme provider when running test #2511

Closed coreybruyere closed 3 years ago

coreybruyere commented 3 years ago

I'm trying to set up some initial tests using enzyme and jest. I'm wrapping my tests using wrappingComponent for both shallow and mount but am getting an error stating:

Test suite failed to run
    TypeError: Cannot read property 'neutral' of undefined

My component is referencing a theme from within a styled component like so:

const Card = styled(Box).attrs(({ theme, ...props }) => ({
    bg: theme.colors.neutral[0],
   ....

This is what my test looks like:

import * as React from 'react';
import { shallow, mount } from 'enzyme';
import toJson from 'enzyme-to-json';

import Card from '../Card';
import { MyThemeProvider } from '../../utils/MyThemeProvider';

const wrapper = shallow(<Card>Card component</Card>, {
  wrappingComponent: MyThemeProvider
});

describe('Card render', () => {
  it('should match snapshot', () => expect(toJson(wrapper)).toMatchSnapshot());
});

describe('', () => {
  it('accepts children', () => {
    const wrapper = mount(<Card>Card component</Card>, { wrappingComponent: MyThemeProvider });
    expect(wrapper.text()).toEqual('Card component');
  });
});

My theme has a value for theme.colors.neutral[0]. What's weird is that I have other test that pass just fine that reference the theme the same way. Unfortunately, I can't share the code base but was hoping someone could share some insight / direction on getting set up with enzyme / jest / styled-components tests.

ljharb commented 3 years ago

(It'd be helpful if you filled out the issue template with what versions of everything you're using; it's there for a reason)

I'm not familiar with styled-components enough to know how it injects props into .attrs(). Can you share the code for MyThemeProvider?

coreybruyere commented 3 years ago
    "enzyme": "^3.11.0",
    "enzyme-adapter-react-16": "^1.15.6",
    "enzyme-to-json": "^3.6.1",
    "jest": "^26.6.3",
    "react": "^16.8.6",
    "styled-components": "^5.0.1",
    "ts-jest": "^26.5.4",

It's not just with components using .attrs(). Also happening on components using the styled API. e.g.

const Checkbox = styled(Box)`margin-left: ${({ theme }) => theme.space.base}`

As for the theme provider:

import React from 'react';
import { ThemeProvider, ThemeProviderProps } from 'styled-components';

const merge = require('deepmerge');

import defaultTheme from '../../../theme';

export type MyThemeProviderProps = React.PropsWithChildren<{}> & Partial<ThemeProviderProps<typeof defaultTheme>>;

const MyThemeProvider = (props: any) => {
  const { children, theme } = props;
  const myTheme = theme ? merge(defaultTheme, theme) : defaultTheme;

  return <ThemeProvider theme={myTheme}>{children}</ThemeProvider>;
};

export default MyThemeProvider;
coreybruyere commented 3 years ago

Update

So this only seems to be an issue when using shallow. I changed:

const wrapper = shallow(<Card>Card component</Card>, {
  wrappingComponent: MyThemeProvider
});

describe('Card render', () => {
  it('should match snapshot', () => expect(toJson(wrapper)).toMatchSnapshot());
});

to

describe('Card render', () => {
  it('should match snapshot', () => {
    const wrapper = mount(<Card>Card component</Card>, { wrappingComponent: HaikuThemeProvider });
    expect(toJson(wrapper)).toMatchSnapshot();
  });
});

Any reason I'm unable to use shallow?

ljharb commented 3 years ago

ok, so it's basically doing "whatever styled-components ThemeProvider does", which seems to be using React createContext: https://github.com/styled-components/styled-components/blob/master/packages/styled-components/src/models/ThemeProvider.js

What does wrapper.debug() print out in the shallow vs the mount case? (enzyme doesn't support or recommend snapshot testing, but .debug() is the closest thing we have to it)

coreybruyere commented 3 years ago

I'm adding debug() like so, this look correct?

const wrapper = shallow(<Card>Card component</Card>, {
  wrappingComponent: HaikuThemeProvider
});

describe('Card render', () => wrapper.debug());

As for enzyme not supporting or recommending snapshot testing is there any reading you could link as for why that is? I'm just trying to adhere to best practices and get some test set up on our design system. First time taking a dive into react testing solutions. So any guides, pointers, or links are really helpful.

ljharb commented 3 years ago

ah, no not like that :-) i mean add console.log(wrapper.debug()) somewhere (also, only create wrappers inside an it or a beforeEach, so they'll fail your tests properly)

Snapshot testing is brittle. The diffs are often too noisy, and developers learn to rubberstamp them, defeating the purpose. The only time they're useful is when you're doing a refactor and it's an explicit requirement to have identical output before/after, and those kinds of refactors are rare.

coreybruyere commented 3 years ago

When trying to run a test like so:

describe('', () => {
  it('TEST', () => {
    const wrapper = shallow(<Card>Card component</Card>, {
      wrappingComponent: MyThemeProvider
    });
    console.log(wrapper.debug());
  });
});

I just see the original error, no debug logging.

ljharb commented 3 years ago

@coreybruyere did you figure it out? console.log(wrapper.debug()); should have logged something.