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

Can't get state of component wrapped in HOC #431

Closed
matthewgertner opened this issue Jun 1, 2016 · 19 comments
Closed

Can't get state of component wrapped in HOC #431

matthewgertner opened this issue Jun 1, 2016 · 19 comments

Comments

@matthewgertner
Copy link

I have a component wrapped in an HOC. I'm mainly interested in testing the inner component, but I'm finding it difficult to do things like access its state since the root component of the wrapper is the HOC. Do I have to export a separate unwrapped version of the component for testing? What is the justification for not exposing state on non-root components?

@matthewgertner
Copy link
Author

Stumbled on #98. Looks like you are doing the same thing I ended up doing (i.e. exporting the unwrapped version as well). I'll close this but IMO it would be interesting to be able to access the state of the wrapped component since then I would really be testing the same thing that is used in the real app (i.e. the HOC wrapper).

@ljharb
Copy link
Member

ljharb commented Jun 1, 2016

I don't think this is something React itself exposes.

@kokill
Copy link

kokill commented Jun 3, 2016

@CosticaPuntaru @matthewgertner this worked for me.
const wrapper = shallow(component, { context: { /* pass context if required */}}); // decorated component
const child = shallow(wrapper.get(0)); // pure component

@matthewgertner
Copy link
Author

@ljharb Not sure I follow. Isn't state just a normal property of the component?

@matthewgertner
Copy link
Author

@kokill I need to use mount since my tests rely on lifecycle methods.

@ljharb
Copy link
Member

ljharb commented Jun 4, 2016

@matthewgertner yes but of the wrapper component. I don't think you can get at the wrapped component's state.

@aweary
Copy link
Collaborator

aweary commented Jun 4, 2016

It's usually better to do bottom up (leaf nodes to root) instead of top down testing anyways. You should test your presentational (or whatever sits at the bottom of your component tree) components, mocking any props that they require. Then move up the tree and do the same for their parent components.

This is why shallow rendering is so popular. Reaching down into the component tree to validate deep child state is confusing (and difficult as @ljharb mentions). If you validate in isolation at each level then you get an implied contract that the entire tree should render as expected.

For components wrapped in HOCs we usually recommend exporting the unwrapped component and mocking any props that the HOC is providing. If you want to test the HOC itself you are allowed to query props on a child component, which should be enough if you're using composition.

@matthewgertner
Copy link
Author

Fair enough, that's the approach I ended up taking and it looks good so far. Thanks!

@dabit1
Copy link

dabit1 commented Feb 1, 2018

I finally found a very good solution to get a wrapper from a decorated component. For shallowed components you can use dive() but there is no similar method for mounted components. This is the solution I did:

const wrapper = mount(shallow(<MyDecoratedComponent />).get(0))

Works like a charm :)

@jstray
Copy link

jstray commented Feb 16, 2018

I'm having this issue too. I have to wrap the component under test to provide the context:

      wrapper = mount(
        <DragDropContextProvider>
          <MyComponent>
        </DragDropContextProvider>);

MyComponent will not mount if it's not wrapped inside DragDropContextProvider, but I need to get its state to test certain things. That state clearly exists somewhere, because MyComponent is running as expected in the rest of the tests, but I can't get at it.

@abtrumpet
Copy link

I can confirm that @dabit1 solution works. Unfortunately, in my circumstance, my company requires us to use javascript decorators, so I can't export the base component for testing.

@mgerring
Copy link

Hi @dabit1 , it's not clear to me what <MyComponent /> is in this example.

I have a component that looks something like this in the test:

<Provider store={mockStore}>
  <MyComponent ...props></MyComponent>
</Provider>

I attempted to use what I thought your solution was suggesting:

wrapper = mount(shallow(
<Provider store={mockStore}>
  <MyComponent ...props></MyComponent>
</Provider>
).get(0))

...and wound up with the same error I had before I was wrapping the component with Provider. Am I missing something here?

@dabit1
Copy link

dabit1 commented Feb 22, 2018

@mgerring My solution is for decorated components. Is your component decorated? If not, I think just with mount() should works:

mount(
  <Provider store={mockStore}>
    <MyComponent ...props></MyComponent>
  </Provider>
)

What error do you have exactly?

@AntonioRedondo
Copy link

@dabit1 I have the same very issue with the example you show. shallow() cannot be used in my test. How do you access the state of MyComponent?

@dabit1
Copy link

dabit1 commented Apr 27, 2018

@AntonioRedondo can you show me your test?

@AntonioRedondo
Copy link

It's exactly like the example you put three comments above.

As @mgerring says, it looks like there is no way to get the state of a component when using mount() if it's not the root. And this is an issue when <Provider> is used as root.

If I call .state() on anything apart the root I receive the error: ReactWrapper::state() can only be called on the root. And .dive() can only be called on shallowed rendered components, thing that I cannot use because the test is huge and it would break rest of dozens of tests.

@dabit1
Copy link

dabit1 commented Apr 27, 2018

@AntonioRedondo so in my first comment you will find the solution. You have to mount a react element from shallowed component.

@swashata
Copy link

swashata commented Apr 30, 2018

For a decorative HOC (while using react-jss), this is the solution I am using

Edit: I think it would work for decorative syntax too. I haven't tried.

Foo.jsx

import React from 'react';
import injectSheet from 'react-jss';
const styles = ({ color }) => ({
    styled: {
        background: color.bg,
        color: color.fg,
    }
});

class Foo extends React.Component {
    state = {
        foo: 'Foo',
    };

    render() {
        <div className=`foo ${props.classes.styled}`>Foo</div>
    }
}

export injectSheet(styles)(Foo);

Foo.spec.jsx

// import react, enzyme etc
import Foo from 'Elements/Foo';
import { ThemeProvider } from 'react-jss';
const wrapper = mount(
    <ThemeProvider theme={{ color: { bg: '#000000', fg: '#ffffff' } }}>
        <Foo />
    </ThemeProvider>
);
const componentInstance = wrapper
    .childAt(0) // could also be .find(Foo)
    .instance();

// do testing with componentInstance.state.something or componentInstance.props.something
test('something', () => {
    expect(componentInstance.state.foo).toBe('Foo');
});

I hope it helps.

@AGMETEOR
Copy link

shallow(<MyComponent.WrappedComponent) {...props} />)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests