question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

No response when using `jest.useFakeTimers()` with axios and msw

See original GitHub issue

Describe the bug

My app uses Svelte with Axios to fetch data from a REST API endpoint. I noticed that Axios didn’t sometimes wait long enough for the request and it timed out. Now I increased the Axios’ timeout to 12000 ms and wanted to verify it with tests that it actually waits for long enough for the request.

So I mocked the API with msw for the basic test that it actually works. No problems there. And when I add the delay to my msw mocked API, it works too, I just need to increase the test’s timeout value so it actually waits long enough.

Which brings me to my point: I don’t really want to wait for that long to verify that the Axios’ 12 second timeout actually works. So here’s where I wanted to use the jest’s useFaketimers method but I just can’t get it to work. Even when trying to advance the mocked timer with every command I found in jest, the response never comes.

Environment

  • msw: 0.35.0
  • nodejs: 14.17.3
  • npm: 7.24.0
packages.json deps
"devDependencies": {
  "@jest/types": "^27.2.5",
  "@testing-library/dom": "^8.10.1",
  "@testing-library/jest-dom": "^5.14.1",
  "@testing-library/svelte": "^3.0.3",
  "@tsconfig/svelte": "^2.0.1",
  "@types/jest": "^27.0.2",
  "@types/node": "^16.11.6",
  "@types/testing-library__jest-dom": "^5.14.1",
  "@typescript-eslint/eslint-plugin": "^4.33.0",
  "@typescript-eslint/parser": "^4.33.0",
  "eslint": "^7.32.0",
  "eslint-import-resolver-typescript": "^2.5.0",
  "eslint-plugin-import": "^2.25.2",
  "eslint-plugin-svelte3": "^3.2.1",
  "eslint-plugin-testing-library": "^4.12.4",
  "jest": "^27.3.1",
  "msw": "^0.35.0",
  "svelte": "^3.44.0",
  "svelte-check": "^2.2.7",
  "svelte-jester": "^2.1.5",
  "svelte-preprocess": "^4.9.8",
  "ts-jest": "^27.0.7",
  "ts-node": "^10.4.0",
  "tslib": "^2.3.1",
  "typescript": "^4.4.4"
},
"dependencies": {
  "axios": "^0.24.0"
}

Please also provide your browser version.

Not really browser related but sure:

  • Chrome 95.0.4638.69 (Official Build) (64-bit)
  • Firefox 94.0.1 (64-bittinen)

To Reproduce

Steps to reproduce the behavior:

  1. use axios to fetch data from a REST API endpoint
  2. mock the API with msw
  3. add a delay to the msw mocked API
  4. use jest.useFakeTimers() to mock timers
  5. use jest.advanceTimersByTime() to pass the time forward until there should be an answer from the delayed API request
  6. the test times out with an error like Unable to find an element with the text or similar

Expected behavior

jest successfully mocks timers and skips the time so the test doesn’t timeout.

Reproduction repo

https://github.com/asode/msw-usefaketimers

Failing test code: https://github.com/asode/msw-usefaketimers/blob/main/src/App.test.ts#L43

Failing test run: https://github.com/asode/msw-usefaketimers/runs/4106445180#step:4:27

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:4
  • Comments:14 (5 by maintainers)

github_iconTop GitHub Comments

4reactions
kettanaitocommented, Nov 10, 2021

As we’re using timers instead, I’d expect that Jest’s fake timers had no effect on the library. We need to take a deeper dive into how jest.useFakeTimers() actually works, what modules it stubs, etc.

I believe that historically, the main purpose to use the custom timers package was to opt-out from using setTimeout directly. MSW wraps all mocked responses in setTimeout and uses either the custom delay duration or 0 when no delay is intended. It’s because of that zero that we still needed to allow immediate mocked responses when using fake times in Jest.

To give you a better perspective, this code will never resolve if we use setTimeout directly:

it('some test', async () => {
  server.use(
    rest.get('https://example.com', (req, res, ctx) => res(ctx.text('hello')))
  )

  jest.useFakeTimers()

  // This promise will never resolve, as nothing has advanced the fake timers,
  // so the "setTimeout(sendMockedResponse, 0)" is never called. 
  const res = await fetch('https://example.com')
})

We may omit the setTimeout usage for no-delay scenarios, which should allow us to both respect the fake timers (and demand you advance them to support delayed responses) and make immediate responses happen correctly while using fake timers.

I’d be thankful if somebody could give that approach a try. Here’s the related logic that uses setTimeout:

https://github.com/mswjs/msw/blob/f6fbd6c94d86d54ee13d20f0128589332cbddcbf/src/utils/handleRequest.ts#L122-L130

  1. Remove the timers package from rollup.config.js.
  2. Use setTimeout only when the custom delay has been sent.
1reaction
JBudnycommented, Dec 2, 2021

Hi, I feel like my issue may be related to this so I post it here and maybe it wil help someone.

I had a problem while testing the React Native TextInput component with debouncing and redux-toolkit useQuery hook (for data fetching) and I’ve managed to test it this way. It seems kind of hacky though

I upgraded jest to the latest “^27.4.2” version and used it as below.

beforeEach(() => {
	jest.useFakeTimers('legacy')
})

afterEach(() => {
	jest.useRealTimers()
})

test('Search component should display fetched data', async () => {
	const { getByPlaceholderText, findByText } = renderWithProviders(
		<Search />
	)
	const textInput = getByPlaceholderText('Type to search')
	// I had to use act on this fireEvent because jest was asking for it
	// and jest.advanceTimersByTime and similar utilities were not helping.
	// Querying a fireEvent result with await waitFor was also not helping.
	act(() => {
		fireEvent.changeText(textInput, 'Some text')
	})
	// this expects works even without await, but not with 
	// jest.advanceTimersByTime and getByText
	expect(await findByText('some result')).toBeDefined()
})
Read more comments on GitHub >

github_iconTop Results From Across the Web

React MSW axios testing with jest doesnt't get triggered
The reason MSW is not kicking in here is because you are using jest to fully mock axios. So MSW should work if...
Read more >
React Testing Library and the “not wrapped in act” Errors
Case 1: Asynchronous Updates. Test code somehow triggered a testing component to update in an asynchronous way. Here is an example: const MyComponent...
Read more >
Mocking | Guide - Vitest
We use Tinyspy as a base for mocking functions, but we have our own wrapper to make it jest compatible. Both vi.fn() and...
Read more >
React.js Testing Tutorial #6 - Mock Service Worker - YouTube
Mock HTTP calls using Fetch or Axios - Mock Service Worker ... test and add delay to MSW response 11:05 How to se...
Read more >
testing node api with jest
fn (). config. Next, you want to add tests scripts to your package. Document(s) will be added to the empty collection (article collection),...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found