Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React test with enzyme TypeError: Cannot read property 'state' of undefined

Tags:

I'm trying to do unit testing to a component using enzyme shallow rendering. Trying to test state activeTab of the component and it throws TypeError: Cannot read property state. my component Accordion. Accordion component jsx code

 class Accordion extends Component {
    constructor(props) {
        super(props)
        this.state = {
            activeTab: 0
        }
    }

    static defaultProps = {
        tabs: [{title: 'Status'}, {title: 'Movement'}]
    }

    render() {
        const { tabs } = this.props
            , { activeTab } = this.state
        return (
            <div className={`accordion`}>
                {tabs.map((t, i) => {
                    const activeClass = activeTab === i ? `accordion--tab__active` : ''
                    return(
                        <section key={i} className={`accordion--tab ${activeClass}`}>
                            <header className={`accordion--header`}>
                                <h4 className={`accordion--title`}>
                                    <button onClick={() => {this._selectAccordion(i)}}>{t.title}</button>
                                </h4>
                            </header>
                            <div className="accordion--content">
                                {t.title}
                                Content
                            </div>
                        </section>
                    )
                })}
            </div>
        )
    }
    _selectAccordion = activeTab => {this.setState({activeTab})}
}

export default Accordion

and Accordion.react.test.js

import { shallow } from 'enzyme'
import Accordion from './components/Accordion'

test('Accordion component', () => {
    const component = shallow(<Accordion name={`Main`}/>)
    expect(component.state('activeTab')).equals(0)
})
like image 223
Wimal Weerawansa Avatar asked Sep 25 '17 10:09

Wimal Weerawansa


2 Answers

This could be a this scoping issue. With event handlers in React, you have to bind the event handler in the constructor to "this". Here is some info from React's docs about it:

You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.

This is not React-specific behavior; it is a part of how functions work in JavaScript. Generally, if you refer to a method without () after it, such as onClick={this.handleClick}, you should bind that method.

class Accordion extends Component {
    constructor(props) {
        super(props)
        this.state = {
            activeTab: 0
        }

        // This binding is necessary to make `this` work in the callback
        this._selectAccordion = this._selectAccordion.bind(this);
    }

    static defaultProps = {
        tabs: [{title: 'Status'}, {title: 'Movement'}]
    }

        _selectAccordion(activeTab){
            this.setState({activeTab : activeTab})
        }

    render() {
        const { tabs } = this.props,
        { activeTab } = this.state
        return (
            <div className={`accordion`}>
                {tabs.map((t, i) => {
                    const activeClass = activeTab === i ? `accordion--tab__active` : ''
                    return(
                        <section key={i} className={`accordion--tab ${activeClass}`}>
                            <header className={`accordion--header`}>
                                <h4 className={`accordion--title`}>
                                    <button onClick={() => {this._selectAccordion(i)}}>{t.title}</button>
                                </h4>
                            </header>
                            <div className="accordion--content">
                                {t.title}
                                Content
                            </div>
                        </section>
                    )
                })}
            </div>
        )
    }

}
like image 170
Dream_Cap Avatar answered Sep 30 '22 01:09

Dream_Cap


Your tests should verify how the component works but not "how to change a state". You need to throw new props into your component and get a result, and the result is expected.

I've tested my components with snapshots

This is an example of my current project

describe('<Component />', () => {
  it('Page rendered', () => {
    const rendered = renderComponent({
      ...testProps,
      loadDataList,
      loading: true,
    });

    expect(rendered).toMatchSnapshot();
  });
});
like image 22
Daniel Rosenberg Avatar answered Sep 30 '22 01:09

Daniel Rosenberg