test-utils icon indicating copy to clipboard operation
test-utils copied to clipboard

Unable to use with `msw` to mock requests

Open rinux55 opened this issue 1 year ago • 6 comments

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

rinux55 avatar Mar 14 '24 15:03 rinux55

Linking https://github.com/unjs/ofetch/issues/295 here as well

rinux55 avatar Mar 22 '24 08:03 rinux55

As a workaround you can try to use registerEndpoint function.

ExEr7um avatar Apr 14 '24 12:04 ExEr7um

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.

https://github.com/nuxt/test-utils/compare/v3.10.0...v3.11.0#diff-aed062a1fdd005bf79a0cf1ce9791008fa96eb4b91dbcd9e85d1820b6257450a

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 avatar Jul 02 '24 20:07 bcorey85

@bcorey85 Where would you add that workaround?

david-mears-2 avatar Jul 31 '24 17:07 david-mears-2

@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
    }
}

bcorey85 avatar Jul 31 '24 17:07 bcorey85

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.

shunnNet avatar Aug 25 '24 06:08 shunnNet

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 avatar Oct 29 '24 12:10 rinux55

@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 avatar Jun 02 '25 22:06 matthew-dean

@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 avatar Jun 03 '25 14:06 rinux55

@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 avatar Jun 26 '25 07:06 paulmelero

@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 avatar Jun 29 '25 20:06 matthew-dean

@matthew-dean what Nuxt version are you using?

david-mears-2 avatar Jun 30 '25 16:06 david-mears-2

@david-mears-2 I'm on 3.17.5, but I think I was on 3.15.4 when testing this

matthew-dean avatar Jun 30 '25 20:06 matthew-dean