How to Test VueRouter With Vitest

Vitest is a fast, lightweight, and modern unit testing framework for JavaScript and TypeScript, designed with a focus on simplicity, speed, and ease of use. Built on top of Vite, a popular build tool, Vitest takes advantage of Vite's fast development server to provide near-instant test execution.

Web Development

In this article we will test Vue Router with Vitest. As it's a small, testing application we'll use real Vue Router. Instead of using real Vue Router there is a way to create mock version of router. That approach allow as to test only these features that we want to and its recommended for more complex applications

Create a sample application using Vue Router and the Composition API

WelcomePage.vue

Inside our <script setup> , we use the useRouter functions. To get the user data we access to store by call useStore function.

WelcomePage component contains the button. After clicked on these button onClick function should be called and new router parameters should be pushed.

Depending on whether the user has an ID or not, parameters will be different - name, params and query for user with ID and only name for user without ID.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<script setup>
import { useRouter } from 'vue-router' 
import { useStore } from ' ../stores/index.js' 
import { computed } from 'vue'

const store = useStore()
const router = useRouter()
const userId = store.userId
const userName = store. userName

const buttonText = computed () => {
return userId ? "Go to profile" : "Create account"
})

function onClick() {
  if (userId) {
    router. push ({ 
    name: "User",
    params: { userId: userId },
    query: {
      name: userName,
    },
  }) else {
    router.push({
      name: "CreateAccount",
    })
  }
}
</script>
<template>
<div>
  <h1>Welcome to our website!</h1>
  <button @click="onClick">{{ buttonText }}</button>
</div>
</template>

Let's test the navigation

Setup

  • Import router and store
  • Create a local router
  • Creating a pinia instance

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { describe, it, expect, beforeEach } from 'vitest';
import { mount } from '@vue/test-utils';

import { setActivePinia, createPinia } from 'pinia';

import { useStore } from '../stores/index.js';

import router from '../router';

import { createRouter, createWebHistory } from 'vue-router';
import WelcomePage from '../components/WelcomePage.vue';

// create local router
const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'Welcome',
      component: WelcomePage,
    },
  ],
});

describe('WelcomePage', () => {
  let pinia;
  let wrapper;

  beforeEach(() => {
    // creating a pinia instance
    pinia = createPinia();
    setActivePinia(pinia);
  });
});

Implementation

Implementation steps for both tests are the same.

  • Mock function for router push method
  • Use the testing pinia
  • Set the value for name and id state
  • Mock the routing using the global.mocks property
  • Call onClick function
  • Expect router to be called with proper parameters

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
it('redirect user to User page', async () => {
  const push = vi.spyOn(router, 'push');
  const store = useStore();
  store.userName = 'Jenifer';
  store.userId = 12345;
  
  wrapper = mount(WelcomePage, {
    global: {
      plugins: [router],
    },
  });

  wrapper.vm.onClick();

  expect(push).toHaveBeenCalledTimes(1);
  expect(push).toHaveBeenCalledWith({
    name: 'User',
    params: { userId: 12345 },
    query: {
      name: 'Jenifer',
    },
  });
});

it('redirect user to CreateAccount page', async () => {
  const push = vi.spyOn(router, 'push');
  const store = useStore();
  store.userName = null;
  store.userId = null;

  wrapper = mount(WelcomePage, {
    global: {
      plugins: [router],
    },
  });

  wrapper.vm.onClick();

  expect(push).toHaveBeenCalledTimes(1);
  expect(push).toHaveBeenCalledWith({
    name: 'CreateAccount',
  });
});
UserProfile.vue

In the second component we display the user name. This name comes from the route query, so just like in the previous component, we use the useRouter function. In this test case, we want to check that the computed property returns the correct value and that the HTML element displays it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<script setup>
import { computed } from "vue";
import { useRoute } from "vue-router";

const route = useRoute();

const userName = computed(() => {
  return route.query.name;
});
</script>

<template>
  <h1 class="welcome-message">Welcome {{ userName }}!</h1>
</template>

Now let's write the test.

Setup

  • Import router
  • Create a local router
  • Mock route query value
  • Mock the routing using the global.mocks property

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { describe, it, expect, beforeEach } from 'vitest';
import { shallowMount } from '@vue/test-utils';

import router from '../router';

import { createRouter, createWebHistory } from 'vue-router';
import UserProfile from '../components/UserProfile.vue';

// create local router
const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/user',
      name: 'User',
      component: UserProfile,
    },
  ],
});

router.currentRoute.value.query = {
  name: 'Jenifer',
};

describe('UserProfile', () => {
  let wrapper;

  beforeEach(() => {
    wrapper = shallowMount(UserProfile, {
      global: {
        plugins: [router],
      },
    });
  });
});

Implementation

Everything we need is already configured. Now we just need to check whether the expected behavior is correct.

1
2
3
4
it('userName should return route query name value', async () => {
  expect(wrapper.vm.userName).toEqual('Jenifer');
  expect(wrapper.html()).toContain('Welcome Jenifer!');
});

I hope this article was helpful and you will now be able to test Vue Router in your application. To see the tested example go to app repsitory on github.

Improve your UI/UX

If you need help with Vue Router and Vitest, our team is here to assist you. Whether you're setting up dynamic routes, testing your Vue components, or integrating unit tests with Vitest, we have the expertise to ensure your project runs smoothly. Reach out for tailored solutions and expert guidance on your Vue.js and testing workflows here.

Written by:

 avatar

Ewelina Zwolenik

Frontend Developer

Ewelina is an amazing frontend developer who's great at working with Vue.js, HTML, CSS, and Tailwind CSS. She has a sharp eye for design and knows how to turn ideas into beautiful, responsive websites and apps that look great on any device.

Read more like this: