Reputation: 8395
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
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
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
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
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
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.
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