Skip to content

Instantly share code, notes, and snippets.

@jaredwilli
Forked from alfonsomunozpomer/Fetch.test.js
Created March 8, 2018 17:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaredwilli/2a28766c0a47bd6e1a4fd22000dc8cbb to your computer and use it in GitHub Desktop.
Save jaredwilli/2a28766c0a47bd6e1a4fd22000dc8cbb to your computer and use it in GitHub Desktop.
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!"}`)
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment