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.