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 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 sudheeshcm commented Mar 26, 2018

👍

@shriramshankar

This comment has been minimized.

Copy link

@shriramshankar shriramshankar commented Apr 3, 2018

This was helpful. Thanks!

@connerleverett

This comment has been minimized.

Copy link

@connerleverett connerleverett commented Apr 12, 2018

Thanks!

@magdalko

This comment has been minimized.

Copy link

@magdalko magdalko commented Apr 16, 2018

Helpful!

@mankutila

This comment has been minimized.

Copy link

@mankutila mankutila commented Apr 25, 2018

Thanks a lot! 👍

@gsondur

This comment has been minimized.

Copy link

@gsondur 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 dj-marko commented May 14, 2018

Rick and Morty :)

@schulzzw

This comment has been minimized.

Copy link

@schulzzw 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 winio94 commented Jul 20, 2018

You saved my day, thanks! 👍

@Gbacc

This comment has been minimized.

Copy link

@Gbacc Gbacc commented Sep 7, 2018

Thanks a lot !

@scousino

This comment has been minimized.

Copy link

@scousino 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 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 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