Categories
Vue Testing

Unit Test Vue Apps with Vue Test Utils — Mock External Dependencies

Spread the love

With the Vue Test Utils library, we can write and run unit tests for Vue apps easily.

In this article, we’ll look at how to write unit tests with the Vue Test Utils library.

Test Async Behavior Outside of Vue

To test async behavior outside of Vue, we have to mock the async code before we run our test.

For example, if we have a YesNo.vue component in the components folder:

<template>
  <div>
    <button @click="fetchResults">fetch answer</button>
    <p>{{ answer }}</p>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: "YesNo",
  data() {
    return {
      answer: undefined
    }
  },
  methods: {
    async fetchResults() {
      const { data: { answer } } = await axios.get('https://yesno.wtf/api')
      this.answer = answer;
    }
  }
};
</script>

We can test it by mocking the axios object and put our own get method in the mocked object:

import { shallowMount } from '@vue/test-utils'
import YesNo from '@/components/YesNo'

jest.mock('axios', () => ({
  get: () => Promise.resolve({ data: { answer: 'yes' } })
}))

describe('YesNo tests', () => {
  it('renders answer after clicking fetch answer', async () => {
    const wrapper = shallowMount(YesNo)
    await wrapper.find('button').trigger('click')
    expect(wrapper.text()).toContain('yes')
  })

})

Since axios.get returns a promise with the response, we do the same thing with our mock get method.

Then when we click the button, we should see the 'yes' answer.

Using with Vue Router

We can test our Vue app with Vue Router.

For example, given that we have the following Vue app code:

components/Bar.vue

<template>
  <div>Bar</div>
</template>

<script>
export default {
  name: "Bar",
};
</script>

components/Foo.vue

<template>
  <div>Foo</div>
</template>

<script>
export default {
  name: "Foo",
};
</script>

main.js

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import Foo from '@/components/Foo';
import Bar from '@/components/Bar';

const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]

const router = new VueRouter({
  routes
})

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

We can test the Foo component by writing:

import { shallowMount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
import Foo from '@/components/Foo';

const localVue = createLocalVue()
localVue.use(VueRouter)
const router = new VueRouter()

describe('Foo tests', () => {
  it('renders the Foo component', async () => {
    const wrapper = shallowMount(Foo, {
      localVue,
      router
    })
    expect(wrapper.text()).toContain('Foo')
  })

})

We create a local Vue instance with the createLocalVue function.

Then we call use to add the VueRouter plugin.

Also, we create a new VueRouter instance.

Then we pass that all in when we mount our component so that we can test it in isolation.

Testing components that use router-link or router-view

There are a few ways to test components that use the router-link or router-view components.

One way is to stub those components by putting the component names in the stubs array:

import { shallowMount } from '@vue/test-utils'
import App from '@/App';

describe('App tests', () => {
  it('renders the App component', async () => {
    const wrapper = shallowMount(App, {
      stubs: ['router-link', 'router-view']
    })
    expect(wrapper.find('app')).toBeTruthy()
  })

})

We mount our App component from the previous example code with the stub for the router-link and router-view components.

Then we can carry on with our tests.

We can also pass in the localVue object with the local version of the Vue instance that has the VueRouter instance added to it:

import { shallowMount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
import App from '@/App';

const localVue = createLocalVue()
localVue.use(VueRouter)

describe('App tests', () => {
  it('renders the App component', async () => {
    const wrapper = shallowMount(App, {
      localVue
    })
    expect(wrapper.find('app')).toBeTruthy()
  })

})

Conclusion

We can test our app with external dependencies by mocking them so that they can run reliably without relying on external resources.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *