Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing a Redux-connected component using Enzyme #1002

Closed
sa-js opened this issue Jun 23, 2017 · 67 comments

Comments

@sa-js
Copy link

commented Jun 23, 2017

Enzyme

i) What is the least error-prone way to test a Redux-connected Componet using Enzyme?
I have checked many links and articles but haven't found a satisfactory answer. It's confusing me a lot.
ii)How can I test whether my component is getting certain props or not using Enzyme?

Versions

  • React-Boilerplate : Current Version
  • Node/NPM: ^6
  • Browser: Chrome
  • Enzyme: Current Version
@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Jun 23, 2017

const wrapper = shallow(<ConnectedComponent />).dive();

and then make your assertions on the wrapped component.

@ljharb ljharb added the question label Jun 23, 2017

@gecko25

This comment has been minimized.

Copy link

commented Jul 26, 2017

Any update to this? I secondly this very strongly. Everything I have tried does not work.

I set the context like shown in the docs -- http://airbnb.io/enzyme/docs/api/ReactWrapper/setContext.html

This also looked promising
https://stackoverflow.com/questions/37798741/nested-components-testing-with-enzyme-inside-of-react-redux

But usually everything leads me back to:

Invariant Violation: Could not find "store" in either the context or props of "Connect(DatePicker)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(DatePicker)".

😢

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Jul 26, 2017

There's no update needed; wrap your connected component and use .dive().

@gecko25

This comment has been minimized.

Copy link

commented Jul 26, 2017

Two things:

  1. This is not working for me.

const wrapper = shallow(<ConnectedDatePicker />).dive();

I still get the same ole error:

Invariant Violation: Could not find "store" in either the context or props of "Connect(DatePicker)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(DatePicker)".

  1. I need to use mount anyways. If I try something like:

const wrapper = shallow(<ConnectedDatePicker store={store}/>).dive();

I get

Method “props” is only meant to be run on a single node. 0 found instead.

when my test tries to use the API method find. (wrapper.find('#startDate').simulate('focus');)

I am assuming it cannot find it, because it is trying to find an grandchild element of my DatePicker component.

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Jul 26, 2017

I'd generally recommend using only shallow as much as possible, and only making assertions on which components your thing renders - in other words, if A renders B which renders C, your A tests shouldn't know anything about C, and should only be asserting that A renders B with the right props. Your B tests should be asserting things about C.

@gecko25

This comment has been minimized.

Copy link

commented Jul 26, 2017

Right. Makes sense. What I am trying to do is more of an integration test.

I am sort of new to making tests. Would this thinking be more correct:

If my DatePicker component has child components that send events up to my DatePicker Component (for example, when a user selects July 21, 2017 a <div/> is clicked & the event bubbles up and eventually is handled by my DatePicker component's onDateChange function).

For my test, rather than simulating that click event. I should be directly testing my onDateChange function with mock data under various circumstances.

(I apologize if this got off scope a little bit, just trying to understand).

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Jul 26, 2017

Yes, you definitely should be unit-testing your onDateChange.

It's your date picker component's tests' job to ensure that onDateChange is called at the proper time.

@gecko25

This comment has been minimized.

Copy link

commented Jul 26, 2017

thank you, this has been very helpful

@ljharb ljharb closed this Jul 26, 2017

@shakiba

This comment has been minimized.

Copy link

commented Sep 3, 2017

@ljharb Is there any way to mock the inner component? Instead of shallow/dive.

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Sep 3, 2017

@shakiba Mocks make tests more brittle; but shallow rendering is kind of like automocking. Can you elaborate on what you're trying to achieve?

@shakiba

This comment has been minimized.

Copy link

commented Sep 4, 2017

@ljharb The reason that shallow does not work for me is that there are other nested components which I want to be rendered, such as bootstrap-react layout components. Basically instead of including specific components in rendering using dive, I want to exclude few components from being rendered. I solved it using jest.mock, but if you have any other idea please let me.

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Sep 4, 2017

enzyme v3 and React 16 will provide an API for that; short of that, that's probably the best option.

@przemuh

This comment has been minimized.

Copy link

commented Sep 21, 2017

Maybe it will be helpful for someone:

If you want to find if connected component is rendered you can find it by Connect(Component) selector.

Example (StylePicker is connected with the store):

index.js

render() {
        return (
            <div>
                <Header>Choose style</Header>
                <StylePicker {...{ styles }}/>
                <NavigationButtons
                    onPrev={this.goToHome}
                    onNext={this.goToLayoutPicker}
                />
            </div>
        );
    }

index.test.js

    it('should render <StylePicker>', () => {
        expect(shallow(<MainComponent />).find('Connect(StylePicker)')).toHaveLength(1);
    });
@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Sep 22, 2017

@przemuh better would be shallow(<MainComponent />).dive().

@mstorus

This comment has been minimized.

Copy link

commented Sep 27, 2017

@ljharb regarding the question in #1002 (comment), don't we still need to use something like redux-mock-store in order for shallow(<MainComponent />) to work? whether or not we use .dive() won't affect that shallow(<MainComponent />) expects to have a store in either context or props, right?

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Sep 27, 2017

@mstorus correct, it won't affect that.

@dschinkel

This comment has been minimized.

Copy link

commented Sep 29, 2017

sometimes you might have to double dive (someContainer.dive().dive() or whatever before you can start working on the component) depending on how your connected container is setup.

For example lets say you're wrapping your connected container in another wrapper like this, you'll possibly have to double dive I've found because you need to now get past the ReactTimeout HOC and dive to get past the react-redux connect wrapper HOC in order to work with the container.

const TimeoutLive = ReactTimeout(connect(mapStateToProps, {
  fetchLive,
  fetchLivePanel,
  fetchLiveVideo,
  clearLive,
  saveLiveRestart,
})(Live));
@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Sep 29, 2017

Totally correct - you need one .dive() per HOC.

@dschinkel

This comment has been minimized.

Copy link

commented Sep 29, 2017

BAM! now that's what I'm talkin about!

@michaelBenin

This comment has been minimized.

Copy link

commented Oct 4, 2017

Is there any syntactic sugar available for components wrapped with react-router's withRouter method?

Instead of a test like:

shallow(
        <StaticRouter context={{}}>
          <IndexPage />
        </StaticRouter>
      ).dive().length
@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Oct 4, 2017

@michaelBenin .dive() is the sugar.

@trevorwhealy

This comment has been minimized.

Copy link

commented Oct 27, 2017

If you don't need access to the redux store as part of your unit test, don't forget that you can also export the React component individually in addition to exporting the connected variant by default.

export class Component extends React.Component {}

export default connect(({ someReducer }) => ({
  ...
}))(Component)

In your test, just extract the "pure" Component rather than the connected one:

import { mount } from 'enzyme'  
import { Component } from '../components/Component'
...
mount(<Component />)
@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Oct 27, 2017

You shouldn't do that, however, because if it's never used by itself in production, there's no value in testing it by itself. Use .dive().

@trevorwhealy

This comment has been minimized.

Copy link

commented Oct 27, 2017

I wouldn't say that it has no value @ljharb but I also know what you mean. Using shallow with dive was not working for my use case. I got the error: Method “props” is only meant to be run on a single node. 0 found instead

renderComponent = (props = {}) => shallow(<Component {...props} />).dive()

const dispatchMock = jest.fn()
const component = renderComponent({ dispatch: dispatchMock })

component.find('form').simulate('submit')
expect(dispatchMock.mock.calls.length).toBe(1)  
@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Oct 27, 2017

@trevorwhealy that's worth exploring - i don't see any .props() calls in that code, nor is dispatchMock used anywhere - but it doesn't change that exporting the "pure" component just for testing isn't a good practice.

@trevorwhealy

This comment has been minimized.

Copy link

commented Oct 27, 2017

@ljharb sure we can agree on that. updated the code to reflect the implementation more closely.

@glosee

This comment has been minimized.

Copy link

commented Nov 28, 2017

Referencing @gecko25's comment above - I get that same error...

shallow(<Provider store={store}><MyContainer/></Provider>).dive()

...

Invariant Violation: Could not find "store" in either the context or props of "Connect(MyContainer)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(MyContainer)"

The connected component has some internal state that I want to test.

@derwaldgeist

This comment has been minimized.

Copy link

commented Jan 30, 2018

I ended up implementing an additional i18n provider mock that doesn't use redux at all. Did the same for redux and router. I couldn't find any other way to work with shallow() and dive() directly.

@ghost

This comment has been minimized.

Copy link

commented Feb 8, 2018

I have also faced same issue as @glosee and @gecko25 and @ashgaliyev and tried to all the things mentioned over here but could not find a way to wrap my component with store using shallow API.
And getting error of Invariant Violation:could not find store........

Below is my code snippet

renderedView = shallow( <Provider store={store}> <ChangeLogPanel /> </Provider> ).dive();

Error is

Invariant Violation: Could not find "store" in either the context or props of "Connect(ChangeLogPanel)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(ChangeLogPanel)".

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Feb 8, 2018

@ghost

This comment has been minimized.

Copy link

commented Feb 8, 2018

@ljharb
I got solution for above error for my case.
renderedView = shallow( <Provider store={store}> <ChangeLogPanel /> </Provider> )
I was trying to print HTML in console using method console.log(renderedView .html()). it gives me above error but when I try to use console.log(renderedView .text()) then error went away.

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Feb 9, 2018

Use .debug(); .html() does a full render.

@mx781

This comment has been minimized.

Copy link

commented Feb 19, 2018

While @glosee 's solution seems to be a fine workaround for now - I had to use shallow rather than mount (otherwise getting a "wrapper.dive is not a function" error). Is there a way to do a full mount on the parent before diveing in to get a shallow wrapper? What am I missing?

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Feb 20, 2018

If you're using mount, you'd .find down to the component you wanted, and then .mount().

@mjvalade

This comment has been minimized.

Copy link

commented Apr 4, 2018

I went down this rabbit hole as well until I realized that I was importing my component into the test file incorrectly. I didn't need to test the connected part, just the component itself.

YES: import { MyComponent } from '../MyComponentFile';
NO: import MyComponent from '../MyComponentFile';

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Apr 4, 2018

In fact, “correctly” means testing the component you use in production - the connected one.

@mstorus

This comment has been minimized.

Copy link

commented Apr 4, 2018

Providing an example of using redux-mock-store, since this thread still comes up a lot in searches.

This example includes redux-thunk, since it's a common middleware used with Redux (and is mentioned in the Advanced section of the Redux tutorial). If you don't use redux-thunk, just remove that part.

import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';

const store = configureStore([
    thunk,
])();

const wrapper = shallow(
  <MyComponent {...props} store={store} />
).dive();

Tested with "redux-thunk": "^2.1.0" and "redux-mock-store": "^1.5.1".

Note that using .dive() ensures that you are testing the connected component (which is actually used in production), rather than the unconnected component (which may not be directly used in production).

@oakis

This comment has been minimized.

Copy link

commented Apr 13, 2018

I did kind of like @mstorus

const wrapper = shallow(<Navbar store={store} />).dive();

But with the actual store, not with redux-mock-store.

@Alebron23

This comment has been minimized.

Copy link

commented May 21, 2018

So I have these HOCs around my component like so:
export default muiThemeable()(injectIntl(reduxForm({ form: 'DevicesRegisterForm', validate, })(DevicesRegisterForm)));

and I'm trying to simply test my form in the DevicesRegisterForm.
I want to do a wrapper.find('form').simulate('click') in my test file but I'm having a hard time accessing the form.
The part I'm having trouble on is using the dive and provider like so:
const container = shallowWithIntl(<Provider store={store}><DefaultDevicesRegister {...props} /></Provider>).dive().dive().dive();

I just get down to the connect component like so:
<Connect(Form(DevicesRegisterForm)) muiTheme={{...}} handleSubmit={[Function: mockConstructor]} isSubmitting={false} valid={false} onFormSubmit={[Function: mockConstructor]} intl={{...}} initialValues={[undefined]} touchOnBlur={true} touchOnChange={false} persistentSubmitErrors={false} destroyOnUnmount={true} shouldAsyncValidate={[Function: defaultShouldAsyncValidate]} shouldValidate={[Function: defaultShouldValidate]} shouldError={[Function: defaultShouldError]} shouldWarn={[Function: defaultShouldWarn]} enableReinitialize={false} keepDirtyOnReinitialize={false} updateUnregisteredFields={false} getFormState={[Function: getFormState]} pure={true} forceUnregisterOnUnmount={false} form="DevicesRegisterForm" validate={[Function: validate]} />

but If i try to dive past this part, it just keeps returning the same thing as above.

Does anyone know how to get past this so I can access my form in the DevicesRegister component?

@kumar303

This comment has been minimized.

Copy link

commented Jun 29, 2018

When you have a lot of HOC's it gets tricky to manage all the dive() statements. It's a bit hacky but I wrote about a shallowUntilTarget() wrapper we use to dive into the right component: https://hacks.mozilla.org/2018/04/testing-strategies-for-react-and-redux/

@Venugopal46

This comment has been minimized.

Copy link

commented Sep 15, 2018

Below solutions are working for me.

const wrapper = shallow(
  <ConnectedComponent />,
  { context: { store } }
).dive();

const wrapper = shallow(
  <Provider store={store}>
    <ConnectedComponent />
  </Provider>
).dive({ context: { store } }).dive();

const wrapper = shallow(
  <ConnectedComponent store={store} />,
).dive();
@Grsmto Grsmto referenced this issue Oct 10, 2018
2 of 2 tasks complete
@wlycdgr

This comment has been minimized.

Copy link

commented Oct 15, 2018

This worked - thank you! - but, why? The Enzyme documentation on .dive() only states that it "shallow render[s] the one non-DOM child of the current wrapper, and return[s] a wrapper around the result." What am I not understanding/knowing that makes it obvious from this description that calling .dive() will allow access to a component wrapped with Redux's connect()?

@WeishenMead

This comment has been minimized.

Copy link

commented Oct 15, 2018

Redux's connect creates a new higher-order component wrapping the component you want. So dive accessed the one child of the Redux wrapper, which is the actual component you're looking for.

@viksok

This comment has been minimized.

Copy link

commented Oct 18, 2018

@przemuh better would be shallow(<MainComponent />).dive().

had a similar issue.
using dive() does not resolve it, but explicit store={mockStore({})} prop does.
shallow(<MainComponent store={mockStore({})}/>)

with dive() I get such an error:
Invariant Violation: Could not find "store" in either the context or props of "Connect(UserGuideButton)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(UserGuideButton)".

@ljharb

This comment has been minimized.

Copy link
Collaborator

commented Oct 19, 2018

you may need to pass the context manually when using dive, pending a known issue

@BalasubramaniM

This comment has been minimized.

Copy link

commented Oct 27, 2018

Finally, I somehow made it work which may help someone. Just pass your store to your component like said above.

import Home from "../components/Home";
import { store } from "../store";

describe("Home", () => {
	it("Should render component", () => {
		const wrapper = shallow(<Home store={store} />);
		expect(wrapper.length).toEqual(1);
	});
});

But after doing it so, I met an another error called,

Invariant Violation: Browser history needs a DOM

After searching for other answers in SO, I found that, I was using createBrowserHistory throughout out my application, in store, whereas createMemoryHistory is more than enough to test unit test cases.

All the test cases have been passed, after changing it so.

Note: I am just new to Jest and correct me if it go wrong at any specific cases.

@hutber

This comment has been minimized.

Copy link

commented Nov 7, 2018

store

Where did you get the store from?

@ncpope

This comment has been minimized.

Copy link

commented Nov 8, 2018

I too struggled with this one for a bit piecing together what I found on various related results. This is my working test: https://gist.github.com/ncpope/a9da1698d919108f104cd1d7e4bec5e9

I am additionally using Redux Mock Store to achieve these results.

@shorif2000

This comment has been minimized.

Copy link

commented Nov 22, 2018

how do i use shallow with mount. i am trying to do this

@karolisgrinkevicius-home24

This comment has been minimized.

Copy link

commented Mar 26, 2019

I have written the helper which I use once I need the redux connected components to test. Here it is:

import { Provider } from 'react-redux';
import { mount } from 'enzyme';
import configureMockStore from 'redux-mock-store';

const defaultStore = { responsive: { fakeWidth: 1200 } };
const mockedStore = configureMockStore()(defaultStore);

export const mountWithProvider = children => (store = mockedStore) =>
  mount(<Provider store={store}>{children}</Provider>);

Then you're able to use it as follows:

const props = {};
const wrapper = mountWithProvider(<SomeConnectedComponent {...props} />)();
expect(wrapper.exists()).toBe(true);

Also you can set custom state of the store to override the default one defined in the helper scope:

const myCustomState = {
  myName: 'test',
  myAge: 9999
};
const wrapper = mountWithProvider(<SomeConnectedComponent {...props} />)(myCustomState);
@RrNn

This comment has been minimized.

Copy link

commented May 8, 2019

@ljharb this works for me const wrapper = shallow(<ConnectedComponent />).dive();, but I'm curious to know why? My scenario was, a component that has another component as a child, both of which are connected. So the code looks something like

class Session extends React.Component {
...
render() {
<div>
...
<MealSessionModal propOne={propOne} />
...
<div>
}
}

MealSessionModal is connected too, remember.
So in my tests, I did

const wrapper = mount(
    <BrowserRouter>
      <Sessions store={store} {...props} />
    </BrowserRouter>
  )

but this was throwing th error
Invariant Violation: Could not find "store" in either the context or props of "Connect(MealSessionModal)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(MealSessionModal)".
which was pointing to the child MealSessionModal. so I ended up using

const wrapper = shallow(<Sessions store={store} {...props} />).dive()

which worked fine. My other question is, how could I use the mounted parent Sessions in my tests without using shallow and then diving? And why does shallowing and diving work?

sunkibaek added a commit to sunkibaek/react-native-paginatable that referenced this issue Jun 4, 2019

@mmaya

This comment has been minimized.

Copy link

commented Jun 6, 2019

const myCustomState = {
  myName: 'test',
  myAge: 9999
};
const wrapper = mountWithProvider(<SomeConnectedComponent {...props} />)(myCustomState);

Thanks so much! Your solution solved most of my problems :D

@951565664

This comment has been minimized.

Copy link

commented Jun 26, 2019

Two things:

  1. This is not working for me.

const wrapper = shallow(<ConnectedDatePicker />).dive();

I still get the same ole error:

Invariant Violation: Could not find "store" in either the context or props of "Connect(DatePicker)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(DatePicker)".

  1. I need to use mount anyways. If I try something like:

const wrapper = shallow(<ConnectedDatePicker store={store}/>).dive();

I get

Method “props” is only meant to be run on a single node. 0 found instead.

when my test tries to use the API method find. (wrapper.find('#startDate').simulate('focus');)

I am assuming it cannot find it, because it is trying to find an grandchild element of my DatePicker component.

Has the first question been solved? I only saw you discussing mount and shallow,And I still report an error now.

@olyayakimovich

This comment has been minimized.

Copy link

commented Jul 25, 2019

I had the same problem. This solution worked for me:
const wrapper = mount(<Provider store={store}><ConnectedComponent /></Provider>, { context: { store }, childContextTypes: { store: PropTypes.objectOf(PropTypes.any), }, });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.