GTF
GTF

Reputation: 8395

using enzyme.mount().setProps with a react-redux Provider

I have a test which is setting props, to observe some changes in the component. The only complication is that I'm wrapping the rendered element in a <Provider> because there are some connected components further down the tree.

I'm rendering via

const el = () => <MyComponent prop1={ prop1 } />;
const wrapper = mount(<Provider store={store}>{ el() }</Provider>);

I'm then trying to observe some changes by using the following:

wrapper.setProps({ /* new props */ });
// expect()s etc.

The problem is that setProps() is not setting the props properly on the wrapped component. I assume that this is because <Provider> is not actually passing props through as it's not an HoC. Is there a better way to test this than just changing the locally scoped prop variables and re-rendering?

Upvotes: 18

Views: 10785

Answers (5)

Vikas Kumar
Vikas Kumar

Reputation: 2998

Another approach will be to use wrappingComponent.

For example, let say this is your provider

const ExampleProvider = ({ children }) => (
      <Provider store={store}>
        {children}
      </Provider>
    );

and then initialize wrapper as follows

wrapper = mount(<Component />, { wrappingComponent: ExampleProvider});

And then in test cases, you should be able to call wrapper.setProps directly.

Upvotes: 2

wentjun
wentjun

Reputation: 42526

I would like to provide a similar solution to the one above.

With the mount wrapper as defined in the question,

const wrapper = mount(<Provider store={store}>{ el() }</Provider>);

And then, on the test itself, you can simply clone the children, which is the child component within Provider, and then call setProps:

it('should get do x and y', () => {  
  wrapper.setProps({
    children: cloneElement(wrapper.props().children as ReactElement, { ...props }),
  });

  // handle the rest below
});

If you are using JavaScript, there is no need to include the as ReactElement type assertion.

For those who are using TypeScript, it is necessary to assert it as ReactElement, as ReactChild could be of types ReactElement<any> | ReactText. Since we are certain that the element rendered within Provider is a ReactElement, the fastest solution would be to use type assertion.

Upvotes: 0

Mina Djuric
Mina Djuric

Reputation: 604

I had the same issue after wrapping my component in a Provider. What I did is, I used setProps on children instead of a component itself.

This is the example of my component in test suite:

 let component, connectedComponent; 
 component = () => {
 store = configureMockStore()(myStore);  
 connectedComponent = mount(
     <Provider >
       <MyComponent store={store} params={{xyz: 123}} />
     </Provider>);};

But in the test itself, I did this:

connectedComponent.setProps({children: <MyComponent params={{}} />});

Upvotes: 1

soupette
soupette

Reputation: 1280

Here's an approach using the setProps

import { Provider } from 'react-redux';


const renderComponent = properties => mount(
  React.createElement(
    props => (
      <Provider store={store}>
        <IntlProvider locale="en" defaultLocale="en" messages={messages}>
      <Router>
        <MyComponent {...props} />
      </Router>

      </Provider>
    ),
    properties))

Then in your test

it('should set some props', () => {
   const renderedComponent = renderComponent(props);
   renderedComponent.setProps({ /* some props */ } });

  expect(true).toBe(true);
})

Upvotes: 10

random-forest-cat
random-forest-cat

Reputation: 35904

You should only be calling setProps on the wrapped component or parent.

A good rule of thumb is your test should only be testing a single component (the parent), so setting props on children is not permitted with enzyme.

https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/setProps.md#setpropsnextprops--self

If you have child components further down that we need to satisfy store dependencies for (via Provider and context), then that is fine, but those child components should really have their own isolated tests.

That is where you'd be encouraged to write a test for a change on setProps.

If you find yourself writing tests for a container or connector, you would really only want to verify that the child component is receiving the correct props and or state. For example:

import { createMockStore } from 'mocks'
import { shallwo } from 'enzyme'
// this component exports the redux connection
import Container from '../container'

const state = { foo: 'bar' }

let wrapper
let wrapped
let store

beforeEach(() => {
  store = createMockStore(state)
  wrapper = shallow(<Container store={store} />)
  wrapped = wrapper.find('MyChildComponent')
})

it('props.foo', () => {
  const expected = 'bar'
  const actual = wrapped.prop('foo')
  expect(expected).toEqual(actual)
})

One other tip is that it helps to understand the difference between shallow and mount so you aren't needlessly mocking dependencies for children in a test, the top answer here is a good read:

When should you use render and shallow in Enzyme / React tests?

Upvotes: 1

Related Questions