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 state on decorated components #98

Closed
CosticaPuntaru opened this issue Dec 30, 2015 · 17 comments
Closed

testing state on decorated components #98

CosticaPuntaru opened this issue Dec 30, 2015 · 17 comments
Labels

Comments

@CosticaPuntaru
Copy link

using enzyme you can only setState on root component, but many decorators wraps your component, in this manner you cannot set the state of your component,

here is an example using redux, but is the same for other decorators:

import React from 'react';
import {shallow} from 'enzyme';
import {expect} from 'chai';
import {spy} from 'sinon';
import {connect} from 'react-redux';
import { createStore } from 'redux';

import cx from 'classnames';


let store = createStore(() => ({}), ['Use Redux']);


@connect(() => ({}))
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {open: false};
  }

  handleClick() {
    this.setState({
      open: !this.state.open,
    });
  }

  render() {
    return (
      <div onClick={this.handleClick.bind(this)}
           className={cx('MyComponent', {'MyComponent--open': this.state.open}, this.props.className)}>
        <div class="toggle"> click me</div>
        <div className="MyComponent-children">
          {this.props.children}
        </div>
      </div>
    );
  }
}

describe('MyComponent', () => {
  it('should render class MyComponent--open when state.open === true', () => {
    const component = shallow(<MyComponent store={store}/>);
    component.setState({open: true});
    expect(component.hasClass('MyComponent--open')).to.be.true;
  });

  it('should set state.open = true when clicked', () => {
    const component = shallow(<MyComponent store={store}/>);
    component.simulate('click')
    expect(component.state('open')).to.be.true;
  });
});

isn't there a way to create something like getReactComponentOf(selector) that can support setState?

@lelandrichardson
Copy link
Collaborator

Hey @CosticaPuntaru, the shallow renderer is intentionally limited to operating on only the root component so as to make the test more isolated. In the case of decorators or "wrapped" components like this, the wrapped component is not what we want to test.

Internally at Airbnb, we use a pattern like the following:

class MyComponent extends React.Component {
  ...
}
export default connect(MyComponent); // default export. used in your app.
export { MyComponent as PureMyComponent}; // pure component. used in tests

This will work fine with redux's connect function, but won't work out of the box with the decorator syntax. You could open a pull request to redux to have the @connect decorator expose the underlying wrapped component as a static prop like UnderlyingComponent or something like that

Does that help at all?

@CosticaPuntaru
Copy link
Author

i know that you can do that, but:

  • i like using the @annotation syntax
  • sometimes you need to test if your component respondes correctly to decorator, ex:
    in one component i use the decorator clickOutside (or something like that) and i need to test if the component closes correctly. The only way i found was to use renderIntodom and drop the enzyme for that test

@lelandrichardson
Copy link
Collaborator

@CosticaPuntaru if you want to test the full flux lifecycle for a component, you will likely need to use mount instead of `shallow.

@lelandrichardson
Copy link
Collaborator

Closing for now. I don't see any clear path to making this better than it already is. Please reopen if you have suggestions.

@montogeek
Copy link

@lelandrichardson How do you guys test Redux connected components?

@vickenliu
Copy link

@montogeek i am also looking for a way to test connected components
have you figured it out?

@aweary
Copy link
Collaborator

aweary commented May 1, 2016

@montogeek @vickenliu I'd follow @lelandrichardson's initial advice of exporting the pure component alongside the connected component. Then you just have to mock the props that redux would provide.

@montogeek
Copy link

@vickenliu I am following @aweary advice :) If you need to test it with the store, write E2E tests :)

@jonaswindey
Copy link

jonaswindey commented May 16, 2016

Full example can be found here: https://github.com/fshowalter/redux/tree/ccf1416de48ae81c823d7e58b8d3f9f0fa0df5fb/examples/async

(@connect or the connect() HOC is the same)

If you look at /containers/App.js, you'll see that both class App is exported, as export default connect().

This is also described on http://redux.js.org/docs/recipes/WritingTests.html under 'Connected components'

@potapovDim
Copy link

hello , how can i get state of decorated component by DragDropContext ? mount getState() return null

@ConAntonakos
Copy link

Is this still the preferred method? I was able to solve this with @lelandrichardson's advice. Thanks.

@joncursi
Copy link

Is there a way to achieve the dual-export of a pure component and connected component using the @connect decorator syntax?

@ljharb
Copy link
Member

ljharb commented Feb 11, 2017

@joncursi At this point, you no longer need to export the pure component - use .dive() once for each HOC/wrapper/decorator.

@linxiaowu66
Copy link

@joncursi Do you had the answer about your question already ? I also encounter this issue.

@ronghuang0
Copy link

ronghuang0 commented Feb 14, 2017

@ljharb Let's say I want to get the component that is inside of the HOC/decorator. Is there a way that I can pass props into that component? If I use dive() I get the wrapper and if I use .instance() I can get the component, but I would like to get the component function that I can pass props into.

update: oh I can just use .type from the wrapper

@ljharb
Copy link
Member

ljharb commented Feb 14, 2017

@ronghuang0 in that case, you'd need to expose it directly so you could test it directly.

@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(<MyComponent />).get(0))

Works like a charm :)

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

No branches or pull requests