Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
How to test a React component that sets its state in componentDidMount with fetch, and how to mock it, in Jest
// https://github.com/alfonsomunozpomer/react-fetch-mock
import React from 'react'
import fetchMock from 'fetch-mock'
import Enzyme from 'enzyme'
import {shallow, mount, render} from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
Enzyme.configure({ adapter: new Adapter() })
const DELAY_MS = 2000
const sleep = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
const fetchResponseJson = async (url) => {
try {
const response = await fetch(url)
const responseJson = await response.json()
// You can introduce here an artificial delay, both Promises and async/await will wait until the function returns
// await sleep(DELAY_MS)
return responseJson
}
catch (e) {
console.log(`fetchResponseJson failed:`, e)
}
}
class SimpleComponent extends React.Component {
constructor(props) {
super(props)
this.state = { data: null }
}
render() {
return(
<span>{JSON.stringify(this.state.data)}</span>
)
}
// By returning the promise (a call to an async function) we can await componentDidMount
componentDidMount() {
return fetchResponseJson(`http://foo.bar`).then((responseJson) => {
this.setState({
data: responseJson
})
})
}
}
// ------------------------------------- TESTS -------------------------------------
// Manual mocking
// const fetchPromise = Promise.resolve({
// json: () => Promise.resolve({Rick: `I turned myself into a pickle, Morty!`}),
// })
// global.fetch = () => fetchPromise
fetchMock.get(`*`, JSON.stringify({Rick: `I turned myself into a pickle, Morty!`}))
describe(`Mocking fetch`, () => {
test(`fails with synchronous code`, () => {
const responseJson = fetchResponseJson(`http://foo.bar`)
expect(responseJson).not.toHaveProperty(`Rick`, `I turned myself into a pickle, Morty!`)
})
test(`using promises`, () => {
expect.assertions(1)
return fetchResponseJson(`http://foo.bar`).then(
(responseJson) => { expect(responseJson).toHaveProperty(`Rick`, `I turned myself into a pickle, Morty!`) })
})
test(`using async/await`, async () => {
const responseJson = await fetchResponseJson(`http://foo.bar`)
expect(responseJson).toHaveProperty(`Rick`, `I turned myself into a pickle, Morty!`)
})
test(`on a React component that loads data into state in componentDidMount`, async () => {
const wrapper = shallow(<SimpleComponent />)
await wrapper.instance().componentDidMount()
// Much less robust, you need to ensure that the sleeping time is greater than the time it takes to resolve the
// fetch, play with values less than or greater than L18 above to see how the component changes
// await sleep(DELAY_MS - 1000)
// await sleep(DELAY_MS + 1000)
// State can be tested here, but not DOM properties, because setState happens in... the future!
// This is more of an Enzyme thing, I suspect
expect(wrapper.state(`data`)).toHaveProperty(`Rick`, `I turned myself into a pickle, Morty!`)
expect(wrapper.text()).not.toEqual(JSON.stringify({Rick: `I turned myself into a pickle, Morty!`}))
// Force update to sync component with state
wrapper.update()
expect(wrapper.text()).toBe(`{"Rick":"I turned myself into a pickle, Morty!"}`)
})
})
@jee

This comment has been minimized.

Copy link

jee commented Jan 21, 2018

@alfonsomunozpomer this was helpful! Thank you for taking the time to write this gist. Cheers!

@sudheeshcm

This comment has been minimized.

Copy link

sudheeshcm commented Mar 26, 2018

👍

@shriramshankar

This comment has been minimized.

Copy link

shriramshankar commented Apr 3, 2018

This was helpful. Thanks!

@connerleverett

This comment has been minimized.

Copy link

connerleverett commented Apr 12, 2018

Thanks!

@magdalko

This comment has been minimized.

Copy link

magdalko commented Apr 16, 2018

Helpful!

@mankutila

This comment has been minimized.

Copy link

mankutila commented Apr 25, 2018

Thanks a lot! 👍

@gsondur

This comment has been minimized.

Copy link

gsondur commented Apr 30, 2018

Thanks for writing this out with such helpful comments! What version of enzyme are you using in this one?

@dj-marko

This comment has been minimized.

Copy link

dj-marko commented May 14, 2018

Rick and Morty :)

@schulzzw

This comment has been minimized.

Copy link

schulzzw commented Jun 15, 2018

I think if you do
wrapper.update();
then test the props it should work

@winio94

This comment has been minimized.

Copy link

winio94 commented Jul 20, 2018

You saved my day, thanks! 👍

@Gbacc

This comment has been minimized.

Copy link

Gbacc commented Sep 7, 2018

Thanks a lot !

@scousino

This comment has been minimized.

Copy link

scousino commented Jun 7, 2019

Doesn't this code cause componentDidMount to be called twice though?

@abhchand

This comment has been minimized.

Copy link

abhchand commented Sep 19, 2019

Is there any good way to do this with react-testing-library instead of enzyme? Thanks!

@alfonsomunozpomer

This comment has been minimized.

Copy link
Owner Author

alfonsomunozpomer commented Sep 23, 2019

@abhchand I have no experience with react-testing-library. Feel free to leave a link here if you figure it out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.