Vue.js 3 Unit Testing Routing Tutorial

In this Vue tutorial we learn how to test the router and routing.

We cover how to test with a local router and asynchronous routing behavior.

Lesson Video

If you prefer to learn visually, you can watch this lesson in video format.

Lesson Project

If you want to follow along with the examples, you will need an app generated by the Vue CLI that includes Jest as well as the Vue Router .

We’ll also need the following view component.

  • /src/views/About.vue

The project should look similar to the following.

Example: project
project-name/
├── public/
├── src/
|   ├── router/
|   |   └── index.js
|   ├── views/
|   |   └── About.vue
|
├── tests/
|   ├── unit/
|   |   └── example.spec.js
|
└── jest.config.js

It doesn’t matter what’s in the About view, we’re only going to test if the route to it works.

Example: src/views/About.vue
<template>
  <h2>About page</h2>
</template>

To keep things simple we’ll use the example.spec.js file to write our tests in, so it’s safe to delete the code in it.

How to test the Vue Router

Because routing typically innvolves multiple components, testing it usually takes place at the E2E testing level. That said, there are still some unit testing we can do.

note Like with the Vuex testing , we’ll create a local router for each test. This allows us to only specify the route(s) we’re testing each time.

We will want to test navigation to a route. So the steps our test should take might look as follows.

  1. Create a local router with the route we want to test.
  2. Programmatically navigate to a route.
  3. Check if the routing was successful.

As an example, let’s test if we can navigate to the About page of our project. The implementation will follow the steps above.

  1. Import the the About view.
  2. Create a local router with the /about route.
  3. Use the push method to programmatically navigate to it.
  4. Check if the navigation was successful by checking if the About component exists.

    Alternatively, we can check for any matching content in the About component like a string.

Example: tests/unit/example.spec.js
import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'

import App from '../../src/App'
// import About component
import About from '../../src/views/About'

describe('App', () => {
  it('renders a component via routing', () => {
    // create a local router
    // with '/about' route
    const router = createRouter({
      history: createWebHistory(),
      routes: [{
        path: '/about',
        name: 'About',
        component: About
      }]
    })

    // navigate to route
    router.push('/about')

    // mount the App
    const wrapper = mount(App)

    // if the About route exists
    // the routing was successful
    expect(wrapper.findComponent(About).exists()).toBe(true)
  })
})

When we run the test above, it will fail because it needs two extra things.

The first is that we need to install the local router. Since it’s a plugin, we can install it by using the plugins array in the global option of the mount method.

Syntax: global.plugins
const wrapper = mount(App, {
  global: {
    plugins: [router]
  }
})

The second problem is that Vue Router 4 (used by Vue 3) handles routing asynchronously. We’ll need to wait until the router has finished the navigation.

Luckily, the Vue Test Utils gives us the handy isReady method to tell us when it’s ready after we navigate with push. We’ll have to await it, so we also need to mark the test with async .

Syntax: isReady
// mark test with async
it('description', async () => {

  // create local router
  const router = createRouter({ ... })

  // navigate to route
  router.push('/route')

  // await navigation from push()
  await router.isReady()
})

The test should now look as follows.

Example: tests/unit/example.spec.js
import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'

import App from '../../src/App'
import About from '../../src/views/About'

describe('App', () => {
  it('renders a component via routing', async () => {
    // create local router
    const router = createRouter({
      history: createWebHistory(),
      routes: [{
        path: '/about',
        name: 'About',
        component: About
      }]
    })

    // navigate to route
    router.push('/about')

    // await navigation from push()
    await router.isReady()

    // install the local router
    const wrapper = mount(App, {
      global: {
        plugins: [router]
      }
    })

    expect(wrapper.findComponent(About).exists()).toBe(true)
  })
})

This time when we run the test, it should pass.

Output:
PASS  tests/unit/example.spec.js
 App
   √ renders a component via routing (11ms)

Further Reading

For more information on the topics covered in this lesson, please see the relevant sections below.