Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React test componentWillReceiveProps using enzyme

I'm going to test lifecycle functions including componentWillReceiveProps using enzyme.

Before anything else, my component should be wrapped materialUi styles and be connected with redux. Otherwise, there will be bugs in render function because I use material-ui components including FlatButton.

const wrapper = mount(
      <MuiThemeProvider muiTheme={muiTheme}>
         <Provider store={store}>
            <MemoryRouter>
               <MyComponent />
            </MemoryRouter>
         </Provider>
      </MuiThemeProvider>)

// absolutely fail
wrapper.find(MyComponent).setProps({ something })
expect(MyComponent.prototype.componentWillReceiveProps.calledOnce).toBe(true)

So the problem is that I can't use setProps() to MyComponent because enzyme doesn't allow applying non-root component. I'm not able to test componentWillReceiveProps or other necessary parts by changing props.

How can I set/change props of MyComponent so that I can test componentWillReceiveProps?

like image 554
Oscar Avatar asked Mar 23 '17 18:03

Oscar


1 Answers

It is better to test your component in isolation. The problem is that material-ui is passing down its props using React context. You can specify the context of your component this way:

import React from 'react';
import { mount } from 'enzyme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

const wrapper = mount(
  <MyComponent />,
  {
    context: {
      muiTheme: getMuiTheme(),
    },
    childContextTypes: {
      muiTheme: React.PropTypes.object.isRequired,
    }
  }
);

Another thing you need to isolate your component is to remove <Provider>. Instead of testing your connected component try to test component itself the way it is described in Redux docs: Testing Connected Components

Shortly - export both component and connected component, then test component by passing props. Example of the component with exports:

import { connect } from 'react-redux'

// Use named export for unconnected component (for tests)
export class MyComponent extends Component { /* ... */ }

// Use default export for the connected component (for app)
export default connect(mapStateToProps)(MyComponent)

You can now import the undecorated component in your test file like this:

import { MyComponent } from './MyComponent';

And the final test could look this way:

import React from 'react';
import { mount } from 'enzyme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import { MyComponent } from './MyComponent';

test('test component', () => {
  const wrapper = mount(
    <MyComponent />,
    {
      context: {
        muiTheme: getMuiTheme(),
      },
      childContextTypes: {
        muiTheme: React.PropTypes.object.isRequired,
      }
    }
  );

  // Test something
  const p = wrapper.find(...);
  expect(...).toEqual(...);

  // Change props
  wrapper.setProps({foo: 1});

  // test again
  expect(...).toEqual(...);
});
like image 132
ischenkodv Avatar answered Sep 28 '22 00:09

ischenkodv