1

I have a custom hook that downloads a file from an url, it works fine and now I trying to test its behaviour.

const useFileDownload = ({ apiResponse, fileName }) => {
  const ref = useRef(null)
  const [url, setUrl] = useState('')
  const [name, setName] = useState('')

  const download = async () => {
    const { data } = await apiResponse()
    const objectUrl = window.URL.createObjectURL(new Blob([data]))

    setUrl(objectUrl)
    setName(fileName)

    ref.current?.click()
    window.URL.revokeObjectURL(objectUrl)
  }

  return { url, ref, name, download }
}

I call like this on my component

  const { ref, url, download, name } = useFileDownload({
    apiResponse: () => axios.get(pdfUrl, { responseType: 'blob' }),
    fileName: 'my_custom.pdf'
  })
...
...
   // this stay hidden on my component
   <a href={url} download={name} ref={ref} />

And my test

describe('useFileDownload', () => {
  it('should create refs', async () => {
    window.URL.createObjectURL = jest.fn()
    const mockRequest = jest.fn().mockResolvedValue({ data: 'url/to/pdf' })
    const { result } = renderHook(() => useFileDownload({ apiResponse: mockRequest, fileName: 'sample.pdf' }))

    act(() => {
      result.current.download()
    })

    expect(result.current.name).toBe('sample.pdf')
    expect(mockRequest).toBeCalledTimes(1)
  })
})

I'm trying to mock createObjectURL, but it doesn't seem to work, and I don't know if its the right way. If the line containing window.URL fails, then the rest of the code fails on the assertion too.

1 Answer 1

1

There are two ways:

Option 1. Using asynchronous version act() function

Option 2. Using waitForNextUpdate(), see example

Sometimes, a hook can trigger asynchronous updates that will not be immediately reflected in the result.current value. Luckily, renderHook returns some utilities that allow the test to wait for the hook to update using async/await (or just promise callbacks if you prefer). The most basic async utility is called waitForNextUpdate.

import { act, renderHook } from '@testing-library/react-hooks';
import { useFileDownload } from './useFileDownload';

describe('useFileDownload', () => {
  it('should create refs', async () => {
    window.URL.createObjectURL = jest.fn();
    window.URL.revokeObjectURL = jest.fn();
    const mockRequest = jest.fn().mockResolvedValue({ data: 'url/to/pdf' });
    const { result, waitForNextUpdate } = renderHook(() => useFileDownload({ apiResponse: mockRequest, fileName: 'sample.pdf' }));

    // option 1
    // await act(async () => {
    //   await result.current.download();
    // });

    // option 2
    result.current.download();
    await waitForNextUpdate();

    expect(result.current.name).toBe('sample.pdf');
    expect(mockRequest).toBeCalledTimes(1);
  });
});

Test result:

 PASS  stackoverflow/75393013/useFileDownload.test.ts (10.519 s)
  useFileDownload
    ✓ should create refs (21 ms)

--------------------|---------|----------|---------|---------|-------------------
File                | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------------|---------|----------|---------|---------|-------------------
All files           |     100 |       50 |     100 |     100 |                   
 useFileDownload.ts |     100 |       50 |     100 |     100 | 15                
--------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        11.205 s

package version:

"jest": "^26.6.3",
"react": "^16.14.0",
"@testing-library/react-hooks": "^8.0.1"

jest.config.js:

module.exports = {
  preset: 'ts-jest/presets/js-with-ts',
  testEnvironment: 'jsdom',
}
Sign up to request clarification or add additional context in comments.

2 Comments

thanks! I tried the first option and it worked flawlessly, but for some reason option 2 didn't worked for me
@hirosk Updated jest config and package version, please confirm it

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.