enzyme: .update() does not work when testing .setState within componentDidMount
Current behavior
I have the following componentDidMount method in my Overview
class:
componentDidMount () {
const tick = () => {
return Overview.retrieveDevices()
.then(devices => {
this.setState({
devices: devices,
visibleDevices: devices.filter(this.searchDevices),
isLoading: false
})
this.timer = setTimeout(tick, 1000)
})
}
tick()
}
Overview.retrieveDevices() returns a promise with the data from another function that makes a fetch request to my server, the data it returns is an array.
I have the following set in my test suite:
import React from 'react'
import Overview from './Overview'
import Enzyme, { shallow, mount } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
const nock = require('nock')
Enzyme.configure({ adapter: new Adapter() })
describe('Overview.jsx', () => {
it('renders without crashing', () => {
expect(shallow(<Overview />)).toMatchSnapshot()
})
it('calls the componentDidMount function when it is created', () => {
const componentDidMountSpy = jest.spyOn(Overview.prototype, 'componentDidMount')
mount(<Overview />)
expect(componentDidMountSpy).toHaveBeenCalledTimes(1)
componentDidMountSpy.mockRestore()
})
describe('componentDidMount', () => {
let getNock
beforeEach(() => {
jest.useFakeTimers()
getNock = nock('http://localhost:3001')
.get('/api/v1/devices')
})
afterEach(() => {
jest.useRealTimers()
nock.cleanAll()
})
it('sets the devices state to an empty array when retrieve devices returns an empty array', () => {
getNock.reply(200, [])
const wrapper = mount(<Overview />)
expect(wrapper.state('devices')).toEqual([])
})
it('sets the devices state to an array of devices when retrieve devices returns devices', () => {
getNock.reply(200, [{id: '1234'}])
const wrapper = mount(<Overview />)
expect(wrapper.state('devices')).toEqual([])
jest.runOnlyPendingTimers()
wrapper.update()
expect(wrapper.state('devices')).toEqual([{id: '1234'}])
})
})
})
It is the final test that is failing… I get the following error:
expect(received).toEqual(expected)
Expected value to equal:
[{"id": "1234"}]
Received:
[]
Difference:
- Expected
+ Received
- Array [
- Object {
- "id": "1234",
- },
- ]
+ Array []
46 | jest.runOnlyPendingTimers()
47 | wrapper.update()
> 48 | expect(wrapper.state('devices')).toEqual([{id: '1234'}])
49 | })
50 | })
51 | })
at Object.it (src/components/Overview/Overview.spec.jsx:48:40)
Expected behavior
The test to pass
Your environment
Linux, Ubuntu 16.04 yarn 1.3.2 node 8.9.0
API
- shallow
- mount
- render
Version
library | version |
---|---|
Enzyme | 3.3.0 |
React | 16.2.0 |
Adapter
- enzyme-adapter-react-16
- enzyme-adapter-react-15
- enzyme-adapter-react-15.4
- enzyme-adapter-react-14
- enzyme-adapter-react-13
- enzyme-adapter-react-helper
- others ( )
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 8
- Comments: 30 (10 by maintainers)
This is still not working. Most of our state setting is done in componentDidMount. Can someone please help on this
Same issue here. componentDidMount is calling a promise that is calling setState, however, the test still can’t see the updated state.
I’ve inherited this from somewhere else, but we use it when we need to wait for unreachable promises.
I have also run into a similar issue, and the problem is not only in componentDidMount it is in general infact, that any programmatically triggered setState changes don’t take effect on UI, unless called externally in test cases.
same issue with
enzyme@3.7.0
,enzyme-adapter-react-16@1.6.0
andreact@16.4
the only way the test will pass is if I call
wrapper.setState({devices: [{id: '1234'}])
@ljharb Thanks for the call out, I went back and tweaked my test and verified it works without chaining. I’m not sure what happened the first time I wrote the tests and why it seemed to fix the async issues I was having. I also thought it was strange that it didn’t work. I appreciate you monitoring this thread.
I did have to use a setTimeout in conjunction with update() in some tests I wrote yesterday. On submitting the form, the component runs various validations and has two different rounds of setState(). (I know, not ideal)
I don’t know if this is the best solution, but I also had this issue and was able to get the test to see my updated state by putting my assertions inside a
setTimeout
. Maybe you could try something like this?