How to test custom prop validators in Vue.js - Vue.js Tutorials
In this article, I will show you how you can test props and custom prop validators in a simple and performant way, without spying on the console error.
Testing Vue.js component props
In this tutorial, we're going to use Jest and Vue Test Utils to test a NotificationMessage
component. The component has two props:
A message
prop, which uses standard prop validation
props: {
message: {
required: true,
type: String
}
}
And a type
prop, which uses custom prop validation. It must be a string, and any of these values: info
error
success
props: {
message: { ... },
type: {
required: false,
type: String,
default: 'info',
validator: (value) => [
'info',
'error',
' success'
].includes(value.toLowerCase())
}
}
The type dictates the design of our notification message. When rendered, it looks like this:
If we try to mount our component with an invalid type, Vue will warn us and show a warning like this in the console.
Want to learn how to make custom Vue.js prop validators? Watch our free lesson
How to test custom prop validators in Vue.js
I'll show you three ways of testing your custom prop validators.
- Spying on the console error (not recommended)
- Using the prop validator through the vue instance (better, not recommended)
- Using the prop validator without mounting the component (recommended, best performance)
1. Test custom prop validators using a spy (not recommended)
A common solution to testing custom prop validators is to spy on the console
object for the warning output.
// I advise against using this approach
it('does not throw console error when info type', () => {
const spy = jest.spyOn(console, 'error')
mount(NotificationMessage, {
propsData: {
type: 'info',
message: 'I like turtles'
}
})
expect(spy).not.toHaveBeenCalledWith(expect.stringContaining('[Vue warn]: Invalid prop: custom validator check failed for prop "type".'))
})
This technically works, but it is a poor, low-quality test. If we remove the validator completely from our component, the test will still pass, because Vue doesn't throw a warning.
There is a much better way to testing your custom prop validators.
2. Test by using the prop validator through the Vue instance (not recommended)
If you think about it, our prop validator is really just a simple function that accepts an argument and returns a boolean.
type: {
// ...
validator: (value) => [
'info',
'error',
'success'
].includes(value.toLowerCase())
}
We can use and invoke the validator method directly in our tests!
We can access all of the Vue component's options through the $options
property of the instance. And with Vue Test Utils, we can access the instance through wrapper.vm
after it's mounted.
// I advise against using this approach for performance reasons
it('passes with info type', () => {
const wrapper = mount(NotificationMessage, {
propsData: {
message: 'I like turtles!'
}
})
// Extract the validator
const validator = wrapper.vm.$options.props.type.validator
// invoke the validator
expect(validator('info')).toBe(true)
})
This is a much better approach to testing custom prop validators than spying on the console warning.
- We're testing the code directly in isolation.
- If we remove the validator from our component, the test will fail.
- If the warning message changes in a future update, it won't break our tests.
However, there is one thing we can do to significantly improve the performance of this approach.
3. Test by using the prop validator without mounting the component (recommended)
We can significantly improve the performance of our test suite by not mounting the component.
Our components are compiled when we run our tests, which means that we have access to the props object (and the validator) without mounting the component. This means we can write less code and get improved performance, for free.
This is how I would test a Vue.js prop validator without mounting the component:
it('it passes with info type', () => {
const validator = NotificationMessage.props.type.validator
expect(validator('info')).toBe(true))
expect(validator('batman')).toBe(false)
})
Isn't that cool? So to test all of the cases of our validator, I would do the following:
it('it accepts valid type props', function () {
const validTypes = ['info', 'error', 'success']
const validator = NotificationMessage.props.type.validator
validTypes.forEach((valid) => expect(validator(valid)).toBe(true))
expect(validator('batman')).toBe(false)
})
There you go, a simple, reliable way of testing your custom prop validators in Vue.js!
Testing standard prop validation in Vue.js
We can use the same performant approach to test any component prop.
When we test standard prop validation, it's enough to assert that the prop object is configured correctly. We can use Jest's toMatchObject
matcher to make sure a message
prop is defined, required, and must be a string.
it('message prop is required and must be a string', function () {
expect(NotificationMessage.props).toMatchObject({
message: {
type: String,
required: true
}
})
})
This is a solid test case. If we remove or alter the prop configuration in the NotificationMessage
component, our test will catch it and fail.
Want to learn more about testing Vue.js and JavaScript?
I hope you've learned something and feel inspired to back your custom prop validators with tests. I've prepared a companion repository for this article if you want to play around with the examples.
If you want to learn more about testing in JavaScript and Vue.js applications, I recommend that you check out our testing courses:
We also do in-person training and remote Vue.js workshops, if you want to elevate your entire team or yourself.
https://vueschool.io/articles/vuejs-tutorials/how-to-test-custom-prop-validators-in-vuejs/