Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setState value used in componentDidMount is not reflected in Enzyme test

Component.js

import React from 'react'
import request from 'superagent'

export default React.createClass({
    getInitialState() {
        return {cats: []}
    },

    componentDidMount() {
        request('/api', (err, res) => {
            if (err) return;
            this.setState({
                cats: res.body.results
            })
        })
    },

    render() {
        let cats = this.state.cats
        let catsList = (
            <ul>
                {cats.map((c) => <li key={c.id}>cat</li>)}
            </ul>
        )
        return (
            <div>
                {cats.length ? catsList : null}
            </div>
        )
    }
})

Component.test.js

jest.unmock('../app.js')
jest.unmock('superagent')

import React from 'react'
import {mount} from 'enzyme'
import nock from 'nock'
import App from '../app.js'

describe('App', () => {
    let ajaxFn
    beforeEach(() => {
        ajaxFn = nock('http://localhost')
            .get('/api')
            .reply(200, {
                results: [{id: 1}, {id: 2}, {id: 3}]
            })
    })

    it('renders correct number of cats', () => {
        let wrapper = mount(<App />)
        expect(wrapper.state('cats').length).toBe(3)
    })
})

The test does not pass. wrapper.state('cats').length is always 0.

I understand that setState doesn't guarantee to update state immediately, however if I log 'cats' in the component, I can see it updating.

like image 569
Babistalikesflyingonrails Avatar asked Mar 10 '16 21:03

Babistalikesflyingonrails


People also ask

Can setState be used in componentDidMount?

You may call setState() immediately in componentDidMount() . It will trigger an extra rendering, but it will happen before the browser updates the screen.

How does setState react to enzyme?

setState(state) => Self. A method to invoke setState() on the root component instance similar to how you might in the definition of the component, and re-renders. This method is useful for testing your component in hard to achieve states, however should be used sparingly.

How do I test componentDidMount jest?

What to do when you want to test that a function is been called, on componentDidMount() React lifecycle Method. Basically the component code looks like this: state = { randomStateToPopulate: [] }; // Test componentDidMount componentDidMount() { this. randomFunction(); } randomFunction= () => { listRandomData().


2 Answers

If you end up setting state in your component in some context that enzyme doesn't know about, you will have to manually call .update() on the wrapper in order for it to get the updated version of the render tree.

it('renders correct number of cats', () => {
    let wrapper = mount(<App />)
    expect(wrapper.update().state('cats').length).toBe(3)
})
like image 79
Leland Richardson Avatar answered Oct 17 '22 06:10

Leland Richardson


I had a similar problem and it was necessary to return a promise from the it callback and check the expectation in the then method of the promise.

In your case (assuming ajaxFn was a promise, or you could turn it into one) I think this would be approximately:

it('renders correct number of cats', () => {
    let wrapper = mount(<App />) 
    return ajaxFn.then(() => {
        expect(wrapper.state('cats').length).toBe(3);
    }
})
like image 40
Simon Hardman Avatar answered Oct 17 '22 08:10

Simon Hardman