vitest: vue-router's useRoute is not correctly mocked

Describe the bug

When using vitest-suggested testing libraries, I can’t successfully mock useRoute and useRouter from the vue-router package.

I’m using:

Any time I run a component test with a mocked useRoute, the mock returns undefined, and Vue warns about missing injections.

...
[Vue warn]: injection "Symbol(route location)" not found. 
  at <HelloWorld ref="VTU_COMPONENT" > 
  at <VTUROOT>
...
TypeError: Cannot read properties of undefined (reading 'path')
     19| 
     20|   <div class="card">
     21|     <button type="button" @click="count++">count is {{ count }}</button>
       |                     ^
     22|     <p>
     23|       Edit

Reproduction

See a minimal reproduction, using a fresh yarn create vite project here: https://github.com/hidde-jan/vitest-use-route-example/blob/main/src/components/__test__/HelloWorld.test.ts

System Info

System:
    OS: macOS 12.3.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 76.22 MB / 32.00 GB
    Shell: 3.3.1 - /opt/homebrew/bin/fish
  Binaries:
    Node: 16.14.0 - ~/.nodenv/versions/16.14.0/bin/node
    Yarn: 1.22.15 - ~/.nodenv/versions/16.14.0/bin/yarn
    npm: 8.3.1 - ~/.nodenv/versions/16.14.0/bin/npm
  Browsers:
    Chrome: 104.0.5112.101
    Firefox: 102.0.1
    Safari: 15.4
  npmPackages:
    @vitejs/plugin-vue: ^3.0.3 => 3.0.3 
    vite: ^3.0.7 => 3.0.9 
    vitest: ^0.22.1 => 0.22.1 


### Used Package Manager

yarn

### Validations

- [X] Follow our [Code of Conduct](https://github.com/vitest-dev/vitest/blob/main/CODE_OF_CONDUCT.md)
- [X] Read the [Contributing Guidelines](https://github.com/vitest-dev/vitest/blob/main/CONTRIBUTING.md).
- [X] Read the [docs](https://vitest.dev/guide/).
- [X] Check that there isn't [already an issue](https://github.com/vitest-dev/vitest/issues) that reports the same bug to avoid creating a duplicate.
- [X] Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/vitest-dev/vitest/discussions) or join our [Discord Chat Server](https://chat.vitest.dev).
- [X] The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 2
  • Comments: 17 (5 by maintainers)

Most upvoted comments

For me this workaround doesn’t work. Current Versions: @vitejs/plugin-vue: 3.1.0 vite: 3.1.3 vitest: 0.23.4

Should be fixed by https://github.com/vitest-dev/vitest/issues/1919 and https://github.com/vitejs/vite/pull/9860

For now, you can manually add node condition to your config:

{
  resolve: {
    conditions: process.env.VITEST ? ['node'] : []
  }
}

Updated: I did manage to make it work with vi.mock This is what i did for anyone using quasar:

    "overrides": {
        "@vitejs/plugin-vue": "^4.0.0",
        "vite": "^4.0.3",
        "vitest": "^0.26.3"
    }
const mockPush = vi.fn();

vi.mock('vue-router', () => ({
    useRouter: () => ({
        push: mockPush,
        currentRoute: { value: 'myCurrentRoute' },
    }),
}));

This is the error I’m getting:

TypeError: Cannot assign to read only property 'useRoute' of object '[object Module]'
 ❯ src/components/Breadcrumbs/Breadcrumbs.test.ts:31:7
     29|     const wrapper = breadcrumbsFactory();
     30| 
     31|     vi.spyOn(routerExports, "useRoute").mockReturnValue({
       |       ^
     32|       name: "organisation",
     33|       query: { org: "" },

[Vue warn]: injection "Symbol(route location)" not found.
  at <Breadcrumbs ref="VTU_COMPONENT" >
  at <VTUROOT>

I’m facing the same issue as you. I was trying to spy the useRouter() but it trows: Cannot assign to read only property 'useRoute' of object

Also tried with vi.mock and vue-router-mock library, none of those options did work for me

I’m using: Vue 3 composition api Quasar v2

Same issue. vitest version: ^0.24.5

I have tried all sorts of cleaning/restoring functions before and after each test, but they don’t do anything. Mock from the first test overrides the mock definition in the next test executed. At this point, we have to put each test in a separated file to make it work, which is very messy. Is it per design that mocking router once in the file, overrides all later router mocks? Any pointers would be very helpful.

vi.mock calls are hoisted, it is mention in the documentation. There is a cheat sheet in docs that might be useful: https://vitest.dev/guide/mocking.html#cheat-sheet

The easiest way to mock a route for different tests would be, in my opinion:

import * as routerExports from 'vue-router'
const useRouteMock = vi.spyOn(routerExports, 'useRoute')
useRouteMock.mockReturnValue({ name: 'name1' })
useRouteMock.mockReturnValue({ name: 'name2' })

Or if you like vi.mock so much, you can mark is as a spy in vi.mock:

import { useRoute } from 'vue-router'
vi.mock('vue-router', () => ({ useRoute: vi.fn() }))

vi.mocked(useRoute).mockReturnValue({ name: 'name1' })
vi.mocked(useRoute).mockReturnValue({ name: 'name2' })

I have tried all sorts of cleaning/restoring functions before and after each test, but they don’t do anything. Mock from the first test overrides the mock definition in the next test executed. At this point, we have to put each test in a separated file to make it work, which is very messy. Is it per design that mocking router once in the file, overrides all later router mocks? Any pointers would be very helpful.

vi.mock calls are hoisted, it is mention in the documentation. There is a cheat sheet in docs that might be useful: https://vitest.dev/guide/mocking.html#cheat-sheet

The easiest way to mock a route for different tests would be, in my opinion:

import * as routerExports from 'vue-router'
const useRouteMock = vi.spyOn(routerExports, 'useRoute')
useRouteMock.mockReturnValue({ name: 'name1' })
useRouteMock.mockReturnValue({ name: 'name2' })

Or if you like vi.mock so much, you can mark is as a spy in vi.mock:

import { useRoute } from 'vue-router'
vi.mock('vue-router', () => ({ useRoute: vi.fn() }))

vi.mocked(useRoute).mockReturnValue({ name: 'name1' })
vi.mocked(useRoute).mockReturnValue({ name: 'name2' })

Hello again, I’ve tried both of your examples, and nothing works.


import * as routerExports from "vue-router";

vi.spyOn(routerExports, "useRoute").mockReturnValue({
      name: "organisation",
      query: { org: "" },
      path: "/organisations/100047",
      meta: {
        breadcrumbs: [
          { text: "Home", ref: "/" },
          { text: "Search", ref: "/organisation-search" },
          { text: "", ref: "/organisations/100047" },
        ],
      },
      matched: [],
      fullPath: "",
      hash: "",
      redirectedFrom: undefined,
      params: {},
    });

This is the error I’m getting:

TypeError: Cannot assign to read only property 'useRoute' of object '[object Module]'
 ❯ src/components/Breadcrumbs/Breadcrumbs.test.ts:31:7
     29|     const wrapper = breadcrumbsFactory();
     30| 
     31|     vi.spyOn(routerExports, "useRoute").mockReturnValue({
       |       ^
     32|       name: "organisation",
     33|       query: { org: "" },

[Vue warn]: injection "Symbol(route location)" not found.
  at <Breadcrumbs ref="VTU_COMPONENT" >
  at <VTUROOT>