Skip to content
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

Vitest restore mock sets all hoisted mocks to undefined when following documentation #864

Open
rstainsby opened this issue Jun 5, 2024 · 3 comments
Labels
documentation Improvements or additions to documentation pending triage

Comments

@rstainsby
Copy link

rstainsby commented Jun 5, 2024

Environment

  • Operating System: Linux
  • Node Version: v18.20.3
  • Nuxt Version: 3.11.2
  • CLI Version: 3.11.1
  • Nitro Version: 2.9.6
  • Package Manager: [email protected]
  • Builder: -
  • User Config: devtools, modules
  • Runtime Modules: @nuxt/test-utils/[email protected]
  • Build Modules: -

Reproduction

https://stackblitz.com/edit/github-htfm4n?file=README.md

Describe the bug

Nuxt's testing documentation recommends restoring mocks that you have hoisted using vi.hoisted() https://nuxt.com/docs/getting-started/testing#built-in-mocks.

The approach the documentation recommends for this is:

const { useStorageMock } = vi.hoisted(() => {
    return { useStorageMock: vi.fn().mockImplementation(() => {
      return { value: 'mocked storage'}
    })
  }
})

mockNuxtImport('useStorage', () => {
  return useStorageMock
})

// Then, inside a test
useStorageMock.mockImplementation(() => {
  return { value: 'something else' }
})

but when using vitests mock restore, this results in useStorageMock being immediately restored to undefined. So, unless you mock useStorageMock in every test, it will likely cause broken tests. This doesn't feel like the implied behavior - In the example above I expected restoring the mock to reset 'value' to 'mocked storage'.

This happens because mockRestore will restore a mocked function (vi.fn()) to undefined if that mocked function doesn't have an initial value (vi.fn(initalval)). Docs on that are here: https://vitest.dev/api/mock.html#mockrestore.

The solution for this then seems to be to define a initial value for the mocked function. So I propose that the docs are changed to:

const { useStorageMock } = vi.hoisted(() => {
    return { 
        useStorageMock: vi.fn(() => {
          return { value: 'mocked storage' }
        })
     }
})

mockNuxtImport('useStorage', () => {
  return useStorageMock
})

// Then, inside a test
useStorageMock.mockImplementation(() => {
  return { value: 'something else' }
})

I've included a stackblitz with tests that showcase this. More detail is included in the README of the stackblitz.

Additional context

TLDR: If you follow the docs recommendation of calling mockRestore before each test, you will end up with undefined mocks for every mocked Nuxt import that requires different implementations between tests.

Logs

No response

@dosubot dosubot bot added the documentation Improvements or additions to documentation label Jun 5, 2024
Copy link

dosubot bot commented Jun 5, 2024

Hey @rstainsby! 👋 I'm here to help you with any bugs, questions, or contributions you might have. I'm a bot that can assist with all things related to software development.

To address the issue where mockRestore restores a mocked function to undefined, you can define an initial value for the mocked function. Here is an updated version of the documentation example:

const { useStorageMock } = vi.hoisted(() => {
  return { 
    useStorageMock: vi.fn(() => {
      return { value: 'mocked storage' }
    })
  }
})

mockNuxtImport('useStorage', () => {
  return useStorageMock
})

// Then, inside a test
useStorageMock.mockImplementation(() => {
  return { value: 'something else' }
})

This approach ensures that the mocked function has a defined initial value, avoiding the issue where mockRestore restores it to undefined. This solution aligns with the behavior described in the Vitest documentation [1].

To continue talking to Dosu, mention @dosu.

@rstainsby
Copy link
Author

Feel free to dismiss this if you feel this is expected behavior. It's just that, for me, the docs imply that the initial hoisted mock value would be the restore state.

Copy link

dosubot bot commented Jun 5, 2024

To clarify the behavior and avoid confusion, the Nuxt testing documentation can be updated to explicitly state that the initial hoisted mock value is not the restore state unless explicitly defined. Here is a suggested update to the documentation:

## Mocking in Nuxt Tests

When using the `mockNuxtImport` and `mockComponent` helpers, it's important to understand how the hoisted mock values are managed. By default, the initial hoisted mock value is not considered the restore state. This means that calling `mockRestore` will set all hoisted mocks to `undefined` unless you explicitly define the restore state.

### Example

```js
import { vi } from 'vitest';
import { mockNuxtImport, mockComponent } from 'nuxt-vitest-mocks';

vi.hoisted(() => {
  if (!globalThis.__NUXT_VITEST_MOCKS) {
    vi.stubGlobal('__NUXT_VITEST_MOCKS', {});
  }
});

// Define your mocks
mockNuxtImport('someModule', () => ({ someFunction: vi.fn(() => 'initial value') }));
mockComponent('SomeComponent', () => ({ default: vi.fn(() => 'initial value') }));

// Restore mocks
vi.mockRestore(); // This will set all hoisted mocks to `undefined` unless explicitly defined

To avoid issues with mockRestore, ensure that you define the restore state explicitly if needed.


This update clarifies the behavior and helps users avoid potential issues with undefined mocks when using `mockRestore` <sup>[[1]](https://github.com/nuxt/test-utils/blob/main/src/module/plugins/mock.ts)</sup>.


<!-- Dosu Comment Footer -->
*To continue talking to [Dosu](https://dosu.dev), mention @dosu.*

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation pending triage
Projects
None yet
Development

No branches or pull requests

1 participant