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.
Lifecycle Hooks
When we call mount
or shallowMount
to mount our components, the lifecycle methods will be run.
However, the beforeDestroy
or destroyed
methods won’t be triggered unless the component is manually destroyed with wrapper.destroy()
.
The component won’t be automatically destroyed at the end of each test, so we have to clean up any dependencies manually.
Writing Asynchronous Tests
When we write tests, we have to be aware that any changes to reactive properties are done asynchronously.
This means that we have to the test as an async function or use then
and run our test code that runs after the reactive property is updated in the then
callback.
For example, we can write:
import { shallowMount } from '@vue/test-utils'
const Counter = {
template: `
<div>
<button @click="count++">Add up</button>
<p>clicks: {{ count }}</p>
</div>
`,
data() {
return { count: 0 }
}
}
describe('Counter', () => {
it('renders new number when Add up button is clicked', async () => {
const wrapper = shallowMount(Counter)
const button = wrapper.find('button')
const text = wrapper.find('p')
await button.trigger('click')
expect(text.text()).toBe('clicks: 1')
})
})
to await
the trigger
method so that we can check the updated value of the count
reactive property after the update is done.
We can also write:
import { shallowMount } from '@vue/test-utils'
const Counter = {
template: `
<div>
<button @click="count++">Add up</button>
<p>clicks: {{ count }}</p>
</div>
`,
data() {
return { count: 0 }
}
}
describe('Counter', () => {
it('renders new number when Add up button is clicked', () => {
const wrapper = shallowMount(Counter)
const button = wrapper.find('button')
const text = wrapper.find('p')
button.trigger('click')
.then(() => {
expect(text.text()).toBe('clicks: 1')
})
})
})
which is the same code without async and await.
Asserting Emitted Events
We can check which events are emitted when we do something with the component.
To do this, we can call the emitted
method to check which events are emitted.
For instance, we can write:
import { shallowMount } from '@vue/test-utils'
const Foo = {
template: `
<div>
<button @click="$emit('foo')">emit foo</button>
</div>
`,
}
describe('Foo', () => {
it('emits foo event when the emit foo button is clicked', async () => {
const wrapper = shallowMount(Foo)
const button = wrapper.find('button');
await button.trigger('click')
expect(wrapper.emitted().foo).toBeTruthy()
})
})
We have the Foo
component that calls $emit
to emit the 'foo'
event when we click it.
Then in the last line of the test, we call wrapper.emitted()
to check if the foo
event is emitted.
If it’s emitted, then the foo
property should be truthy.
We can also check which payload is emitted. For example, we can write:
import { shallowMount } from '@vue/test-utils'
const Foo = {
template: `
<div>
<button @click="$emit('foo', 123)">emit foo</button>
</div>
`,
}
describe('Foo', () => {
it('emits foo event with payload 123 when the emit foo button is clicked', async () => {
const wrapper = shallowMount(Foo)
const button = wrapper.find('button');
await button.trigger('click')
expect(wrapper.emitted().foo.length).toBe(1)
expect(wrapper.emitted().foo[0]).toEqual([123])
})
})
We check the foo
property’s length
to see how many times it’s emitted.
And the payload is checked by accessing foo
‘s array value and checking the content.
Conclusion
We can check if events are emitted and be aware that lifecycle hooks like destroy
aren’t run automatically.