-
Notifications
You must be signed in to change notification settings - Fork 232
Async hook testing #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
We don't actively prevent testing async behaviour, but your hook would need to make the promise/callback/whatever available so that your test runner can be aware of it and so you can delay your assertions. Can you please share an example of the hook you are trying to test and the rest that is failing and I'll see if I can help get it running green for you. |
@mpeyper : You may refer https://www.npmjs.com/package/react-async#with-usefetch |
useFetch is actually a good example of what I am trying to achieve. I don't have an example to share yet, but I will try to provide one as soon as I can. |
So using import fetch from 'node-fetch'
import { useFetch } from 'react-async'
import { renderHook, cleanup } from 'react-hooks-testing-library'
window.fetch = fetch
describe('useFetch tests', () => {
afterEach(cleanup)
test('should use fetch', (done) => {
const { result, rerender } = renderHook(() =>
useFetch(
'https://swapi.co/api/people/1',
{ Accept: 'application/json' },
{
onResolve: () => {
rerender()
expect(result.current.isLoading).toBe(false)
done()
},
onReject: () => {
done.fail(new Error('request failed'))
}
}
)
)
expect(result.current.isLoading).toBe(true)
})
}) This test passes, however, the "not wrapped in PASS test/useFetch.test.js
● Console
console.error node_modules/react-dom/cjs/react-dom.development.js:506
Warning: An update to TestHook inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act
in TestHook I believe I have tracked the warning down to the To be honest, I'm not sure how we are supposed to wrap the promise resolution in an |
I'm just wondering if this could be improved with a import fetch from 'node-fetch'
import { useFetch } from 'react-async'
import { renderHook, cleanup } from 'react-hooks-testing-library'
window.fetch = fetch
describe('useFetch tests', () => {
afterEach(cleanup)
test('should use fetch', (done) => {
const { result, rerender } = renderHook(() =>
useFetch('https://swapi.co/api/people/1', { Accept: 'application/json' })
)
expect(result.current.isLoading).toBe(true)
result.onUpdate(() => {
expect(result.current.isLoading).toBe(false)
done()
})
})
}) or perhaps import fetch from 'node-fetch'
import { useFetch } from 'react-async'
import { renderHook, cleanup } from 'react-hooks-testing-library'
window.fetch = fetch
describe('useFetch tests', () => {
afterEach(cleanup)
test('should use fetch', () => {
const { result, rerender } = renderHook(() =>
useFetch('https://swapi.co/api/people/1', { Accept: 'application/json' })
)
expect(result.current.isLoading).toBe(true)
return result.waitForUpdate().then(() => {
expect(result.current.isLoading).toBe(false)
})
})
}) Thoughts? |
The example with the callbacks, even if I hate callbacks, would actually allow me to use this library, so I am happy with it. It would be great to have a way to watchForUpdate. This would allow us to use async/await and we wouldn't be forced to use callbacks. I prefer this method, but it might be harder to implement. About the warning that is triggered, there is already a PR allowing to pass async stuff to act that should fix this issue on react so I wouldn't worry about it too much. facebook/react#14853 |
An example that should be harder to test: |
I'm also in favour of Ok, let's see if we cant get something like this working test('should use fetch', async () => {
const { result } = renderHook(() =>
useFetch('https://swapi.co/api/people/1', { Accept: 'application/json' })
)
expect(result.current.isLoading).toBe(true)
await result.waitForUpdate()
expect(result.current.isLoading).toBe(false)
}) I'm also thought might read a bit better if it was like test('should use fetch', async () => {
const { result, nextUpdate } = renderHook(() =>
useFetch('https://swapi.co/api/people/1', { Accept: 'application/json' })
)
expect(result.current.isLoading).toBe(true)
await nextUpdate()
// may need to call `rerender` before `result.current` can updates, or perhaps `nextUpdate` can take care of that?
expect(result.current.isLoading).toBe(false)
}) I've got no idea how much effort this is, but if anyone want to try to implement it, I'm happy to help, otherwise I'll take a look myself next time I have a free evening. |
Anyone interested should jump on that PR and give me some feedback. |
I will not be able to test it until a couple of days, but it seems to be exactly what I needed ;) |
Actually I suggested some changes |
Sorry I didn't meant to close the issue, but to cancel my comment. I'm a noob ;) |
Add `waitForNextUpdate` promise for testing async updates Resolves #10
For anyone else stumbling across this issue: https://testing-library.com/docs/dom-testing-library/api-async/#waitfor |
This solved my issue (mind that this is a custom implementation of useFetch): import "@testing-library/jest-dom"
import { renderHook, waitFor } from "@testing-library/react"
import { rest } from "msw"
import { setupServer } from "msw/node"
import useFetch from "./useFetch"
const pokemonEndpoint = jest.fn()
const handlers = [
rest.get("https://pokeapi.co/api/v2/pokemon", pokemonEndpoint)
]
const server = setupServer(...handlers)
describe("The useFetch hook", () => {
beforeAll(() => server.listen())
afterAll(() => server.close())
it("automatically fetches the given URL when called", async () => {
const { result } = renderHook(() => useFetch("https://pokeapi.co/api/v2/pokemon"))
await waitFor(() => expect(result.current.status).toBe("fetching"))
expect(pokemonEndpoint).toHaveBeenCalled()
})
}) |
Hi, I am wondering how I can test hooks with async behavior. I would like to create hooks that send a request to my api to rerender my component with some fresh data.
I tryied a couple of things and I couldn't find a way to test it. Is it supported by this library?
The text was updated successfully, but these errors were encountered: