Unable to use with `msw` to mock requests
Environment
- Operating System: Darwin
- Node Version: v18.19.1
- Nuxt Version: 3.10.3
- CLI Version: 3.10.1
- Nitro Version: 2.9.3
- Package Manager: [email protected]
- Builder: -
- User Config: devtools
- Runtime Modules: -
- Build Modules: -
Reproduction
https://stackblitz.com/~/github.com/rinux55/nuxt-fetch
See app.test.ts. In the test/setup.ts and test/server.ts there is an msw server being set up with a mocked endpoint.
Describe the bug
To mock requests made using $fetch and useFetch, I expect to be able to use a utility like msw.
Unfortunately, this doesn't seem to work in combination with @nuxt/test-utils.
I've created a reproduction where you can see that globalThis.fetch is mocked just fine, but useFetch or fetch is not:
https://stackblitz.com/~/github.com/rinux55/nuxt-fetch
Any help with this would be greatly appreciated.
Additional context
No response
Logs
No response
Linking https://github.com/unjs/ofetch/issues/295 here as well
As a workaround you can try to use registerEndpoint function.
Just a shot in the dark, but it seems that the addition of vi.resetModules() in @nuxt/test-utils/dist/runtime/entry.mjs is breaking my MSW setup. I'm unable to upgrade past 3.10 due to that change. Commenting out that line makes all of my tests pass as expected.
Edit: I found a workaround to fix our setup:
let mockApi
// Nuxt Vitest environment clears modules after setup, so we're saving a global reference to the first mockApi instance
if (window.mockApi) {
mockApi = window.mockApi
} else {
mockApi = setupServer()
window.mockApi = mockApi
}
@bcorey85 Where would you add that workaround?
@david-mears-2 It's ultimately in my Vitest setupFiles. I have it wrapped in a function that I call when my test environment spins up:
export const setupMockApi = () => {
let mockApi
// Nuxt Vitest environment clears modules after setup, so we're saving a global reference to the first mockApi instance
if (window.mockApi) {
mockApi = window.mockApi
} else {
mockApi = setupServer()
window.mockApi = mockApi
}
const DEBUG = false
mockApi.events.on('request:start', ({ request }) => {
if (DEBUG) {
console.log('Outgoing:', request.method, request.url)
}
})
// Establish API mocking before all tests.
beforeAll(() =>
mockApi.listen({
// Change this to 'warn' if you need to debug a missing API route.
// Otherwise leave it on 'bypass' so it doesn't spam warnings when we don't add unnecessary routes
onUnhandledRequest: DEBUG ? 'warn' : 'bypass'
})
)
// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => {
mockApi.resetHandlers()
})
// Clean up after the tests are finished.
afterAll(() => mockApi.close())
const createMockEndpoint = ({
path,
response,
status = 200,
method = 'get',
baseUrl,
delay,
showConsoleError = false
}: MockResponseParams) => {
const base = baseUrl ?? `${mockApiUrl}/api/1`
const requestPath = `${base}${path}`
if (status >= 400 && status <= 599 && !showConsoleError) {
// Suppress automatic Axios console errors for expected errors
vi.spyOn(console, 'error').mockImplementation(() => {})
}
return mockApi.use(
http[method](requestPath, async () => {
if (delay) {
await mswDelay(delay)
}
return HttpResponse.json(response, { status })
})
)
}
return {
mockApi,
createMockEndpoint
}
}
Hello, I have created a Nuxt module called nuxt-msw that can be used with Nuxt 3 and @nuxt/test-utils to work with msw. You can give it a try and see if it solves your problem.
This is now working. You only need to overwrite nuxt's $fetch instance:
import { createFetch } from 'ofetch'
// your msw server
import { server } from './mocks/server'
beforeAll(() => {
server.listen()
globalThis.$fetch = createFetch()
})
@rinux55 The above solution does not work for me. :/ First, using it in Vitest setup did not work. Next, for whatever reason, MSW stops mocking after 1 test, but I haven't disconnected it, it just won't work with the second mounted component.
ChatGPT suggests that Nuxt is doing something to reset the global environment between tests, but using beforeEach has no effect. More than 1 test fails this patch.
@matthew-dean, for me this is now working without anything extra, no need to overwrite the fetch instance anymore with nuxt. Are you on the latest version of everything? If it still doesn't work, drop me a reproduction and I'll have a look.
@rinux55 were you doing the patching in the setup file? That would explain why it's not accessed on other tests in @matthew-dean case if he was doing it on each test. In the nodejs context, msw uses node/request-interceptor if I'm not mistaken, so it should affect each request.
@paulmelero I tried it in setup and in the test. I tried beforeAll and beforeEach. I couldn't find a magic combo that worked. 🤷♂
@matthew-dean what Nuxt version are you using?
@david-mears-2 I'm on 3.17.5, but I think I was on 3.15.4 when testing this