Vue-Test-Utils Guide: Things You Might Not Know About Vue-Test-Utils

Vue-Test-Utils Guide: Things You Might Not Know About Vue-Test-Utils Thumbnail image

Vue-Test-Utils Guide: Things You Might Not Know About Vue-Test-Utils Table of contents

  1. Use await Vue.nextTick() instead of using a callback inside nextTick()
  2. Use await nextTick() after triggering events
  3. Be aware that using a callback in nextTick() will mean errors are not caught
  4. Get emitted events from a component
  5. Fake an emitted event from a child element
  6. Get all listeners for a component in Vue Test Utils
  7. Stub out transitions
  8. Remember when testing with Vue Router that if you use createLocalVue and use the Router on it, the $route and $router properties are read-only
  9. Get all props with wrapper.props()
  10. If testing on a server and need to test rendered HTML, use render() or renderToString()
  11. Use is() to check the type of a component
  12. Use find() with the name or ref option
  13. Use createWrapper to create a wrapper for a mounted Vue instance or HTML element
  14. Suppress Vue warnings by using the config.silent option
  15. Retrieve the query selector used with find() or findAll()
  16. Use Wrapper method attributes() to get an element attribute list
  17. Use Wrapper method classes() to get an element class list
  18. Check if a wrapper contains an element or component with contains()
  19. Use exists() on a Wrapper or WrapperArray
  20. Do not use find() to search for a component! Use findComponent() instead
  21. You can chain find() calls
  22. Use get() instead of find() when it must find something
  23. Do not use these deprecated functions on Wrapper objects
  24. Use these helper methods to set data on Wrapper or WrapperArray objects
    1. How to use setChecked() in Vue Test Utils
    2. Using setSelected() in Vue Test Utils
    3. How to use setValue() in Vue Test Utils
    4. Using setData() in Vue Test Utils
    5. Using setProps() in Vue Test Utils
  25. You can call filter() on WrapperArray objects
  26. Use is() on WrapperArray objects to check that every wrapper inside it matches the selector
  27. Vue mounting options: data gets merged with existing data
  28. Vue mounting options: supplying a string stubs value is deprecated
  29. Vue mounting options: propsData - Use propsData mounting option to set initial props
  30. Vue mounting options: use the listeners attribute to set a component instance's $listeners object
  31. Vue mounting options: use the parentComponent option to set the component to use as a parent.

    Vue has an excellent testing package called vue-test-utils.

    If you have not yet started testing your Vue applications then I recommend that you check out Vue Test Utils. It is easy to pick up and nice to test with.

    Here are a bunch of Vue-Test-Utils tips, tricks and features that you may or may not be aware of.

    If you learned something from a tip please use the button at the end of each post - it makes it easier for future posts to know what to include!

    Use await Vue.nextTick() instead of using a callback inside nextTick()

    (Vue.nextTick() or wrapper.vm.$nextTick is used to wait until Vue has updated at the next tick, often used with async code or when changing the DOM)

    I think this is quite well known at this point, but when Vue Test Utils first came out a lot of code that used nextTick() would use a callback as its argument:

    1. function someTest(done) {
    2.     // Some code setup here.
    3.  
    4.     Vue.nextTick(function testSomethingAfterNextTick() {
    5.         expect(something).toBe(false);
    6.         done();
    7.     });
    8. }

    A cleaner way to this would be to use await. If nextTick() is called with no arguments then it returns a promise.

    1. async function someTest() {
    2.     // Some code setup here.
    3.  
    4.     await Vue.nextTick();
    5.  
    6.     expect(something).toBe(false);
    7. }

    Things to notice: No calling of done() in the callback, and the function must be marked as async.

    Use await nextTick() after triggering events

    Another common thing to see in tests which use nextTick() after a trigger():

    1. async function someTest() {
    2.     wrapper = mount(Component);
    3.     wrapper.trigger('click');
    4.  
    5.     await Vue.nextTick();
    6.  
    7.     expect(something).toBe(true);
    8. }

    This can be a little cleaner, as trigger() returns Vue.nextTick() anyway (which is a promise).

    So you can get rid of the await nextTick() and just await the trigger call:

    1. async function someTest() {
    2.     wrapper = mount(Component);
    3.     await wrapper.trigger('click');
    4.  
    5.     expect(something).toBe(true);
    6. }

    You can also await the results from the following other calls:

    • setChecked()
    • setData()
    • setSelected()
    • setProps()
    • setValue()
    Be aware that using a callback in nextTick() will mean errors are not caught

    While still on the topic of nextTick() there is one important fact to be aware of. If you have a callback function within nextTick() and an error is thrown it will not show up in the test results.

    To get around this problem there are two things which can be done - see the following code:

    1. // this will not be caught
    2. it('will time out', done => {
    3.     Vue.nextTick(() => {
    4.         expect(true).toBe(false);
    5.         done();
    6.     });
    7. });
    8.  
    9. // the three following tests will work as expected
    10. it('will catch the error using done', done => {
    11.     Vue.config.errorHandler = done;
    12.     Vue.nextTick(() => {
    13.         expect(true).toBe(false);
    14.         done();
    15.     });
    16. });
    17.  
    18. it('will catch the error using a promise', () => {
    19.     return Vue.nextTick().then(function() {
    20.         expect(true).toBe(false);
    21.     });
    22. });
    23.  
    24. it('will catch the error using async/await', async () => {
    25.     await Vue.nextTick();
    26.     expect(true).toBe(false);
    27. });
    Get emitted events from a component

    You can get all emitted events from a component by calling wrapper.emitted().

    1. // Test the 'someEventName' was fired 3 times:
    2. expect(wrapper.emitted().someEventName.length).toBe(3);
    3.  
    4. // Test the payload for an event:
    5. const expectedPayload = '...';
    6. expect(wrapper.emitted().yourEvent[0]).toEqual(expectedPayload);
    7.  
    8. // Test something was not emitted:
    9. expect(wrapper.emitted().foo).toBeFalsy();
    Fake an emitted event from a child element

    If you want to fake an emitted event from a child element, you can do the following:

    1. import { shallowMount } from '@vue/test-utils'
    2. import ParentComponent from '../components/ParentComponent'
    3. import ChildComponent from '../components/ChildComponent'
    4.  
    5. describe('ParentComponent', () => {
    6.     it("displays 'Emitted!' when custom event is emitted", () => {
    7.         const wrapper = shallowMount(ParentComponent);
    8.  
    9.         // next line acts as if the child component emitted 'custom'
    10.         wrapper.find(ChildComponent).vm.$emit('custom');
    11.  
    12.         expect(wrapper.html()).toContain('Emitted!');
    13.     });
    14. })
    Get all listeners for a component in Vue Test Utils

    Get all listeners with the vm.$listeners attribute.

    1. const wrapper = mount(ParentComponent);
    2.  
    3. const listeners = wrapper.vm.$listeners;
    4. const childComponentListeners = wrapper.find(ChildComponent).vm.$listeners;
    Stub out transitions

    Transitions can make testing a pain. Unless you are specifically testing some of the transition functionality, it is often a good idea to stub out the transition.

    1. // stub function which will just render the children:
    2. const transitionStub = () => ({
    3.     render: function(h) {
    4.         return this.$options._renderChildren
    5.     }
    6. })
    7.  
    8. test('should render Foo, then hide it', async () => {
    9.     const wrapper = mount(Foo, {
    10.         stubs: {
    11.             // Stub out the transition:
    12.             transition: transitionStub()
    13.         }
    14.     })
    15.  
    16.     expect(wrapper.text()).toMatch(/Foo/)
    17.  
    18.     wrapper.setData({
    19.         show: false
    20.     })
    21.     await wrapper.vm.$nextTick()
    22.  
    23.     expect(wrapper.text()).not.toMatch(/Foo/)
    24. })
    Remember when testing with Vue Router that if you use createLocalVue and use the Router on it, the $route and $router properties are read-only

    If you start to test things involving the Vue Router then you will often end up using createLocalVue:

    1. import { shallowMount, createLocalVue } from '@vue/test-utils'
    2. import VueRouter from 'vue-router'
    3.  
    4. const localVue = createLocalVue()
    5. localVue.use(VueRouter)
    6.  
    7. shallowMount(Component, {
    8.     localVue
    9. })

    Remember that when you do that, it makes the $route (current route) and $router (the router itself) read-only. If you have used the localVue.use(VueRouter) and then use that when mounting, you will not be able to set mocked route data.

    The following code is an example of code which will not work!

    1. import { shallowMount, createLocalVue } from '@vue/test-utils'
    2. import VueRouter from 'vue-router'
    3.  
    4. const localVue = createLocalVue()
    5. localVue.use(VueRouter)
    6.  
    7. const $route = {
    8.     path: '/some/path'
    9. }
    10.  
    11. const wrapper = shallowMount(Component, {
    12.     localVue,
    13.     mocks: {
    14.         // $route does not work! It was set to read only when using localVue.
    15.         $route
    16.     }
    17. })
    Get all props with wrapper.props()

    This is probably quite widely known, but you can access a component's props with the following:

    1.  const wrapper = mount(SomeComponent, {
    2.     propsData: {
    3.       name: 'webdevetc',
    4.     }
    5. });
    6.  
    7. expect(wrapper.props().name).toBe('webdevetc');
    If testing on a server and need to test rendered HTML, use render() or renderToString()

    render() uses vue-server-renderer under the hood, to render a component to static HTML - this is included in the @vue/server-test-utils package.

    1. const wrapper = await render(SomeComponent);
    2.  
    3. expect(wrapper.text()).toContain('<div></div>');

    There is also a renderToString() function.

    Use is() to check the type of a component

    Sometimes you need to check that the output of mount() or something from find() is a certain component type. For that, you can use is.

    1. const wrapper = mount(SomeComponent);
    2.  
    3. expect(wrapper.is(SomeComponent)).toBe(true);
    4. expect(wrapper.find('.modal').is(ModalComponent)).toBe(true);
    Use find() with the name or ref option

    The find() function has the ability to search for elements with a certain Vue component ref identifier, or by name.

    1. const wrapper = mount(SomeComponent);
    2.  
    3. const btn = wrapper.find({ ref: 'myButton'});
    4. const input = wrapper.find({ name: 'myInput'});
    Use createWrapper to create a wrapper for a mounted Vue instance or HTML element

    createWrapper() creates a Wrapper for a mounted Vue instance, or an HTML element.

    1. import { createWrapper } from '@vue/test-utils'
    2. import Foo from './Foo.vue'
    3.  
    4. const Constructor = Vue.extend(Foo)
    5. const vm = new Constructor().$mount()
    6. const wrapper = createWrapper(vm)
    7. expect(wrapper.vm.foo).toBe(true)
    Suppress Vue warnings by using the config.silent option

    You could argue that you shouldn't do this. But to avoid notifications of your tests mutating props or other warnings you can use the config.silent = true option.

    1. import { config } from '@vue/test-utils';
    2. config.silent = true;
    Retrieve the query selector used with find() or findAll()

    After using find() (or findAll()) you may be in the situation where you want to check the query selector used to find the result. You can access this with the selector property.

    (I haven't seen any useful application of this, but it is useful to know for debugging.)

    1. const wrapper = mount(Component);
    2. const link = wrapper.find('li > a');
    3.  
    4. expect(link.selector).toBe('li > a');
    Use Wrapper method attributes() to get an element attribute list

    You can use the attributes() function on a wrapper object to get the DOM node attributes.

    If you provide an argument then it will return that attribute key value.

    1. const wrapper = mount(InputComponent);
    2.  
    3. expect(wrapper.attributes().placeholder).toBe('Enter your email');
    4.  
    5. // use with argument:
    6. expect(wrapper.attributes('placeholder')).toBe('Enter your email');
    Use Wrapper method classes() to get an element class list

    The classes() method on a wrapper object will return the classes for the DOM node.

    If an argument is provided then it will return true or false if the element has that class.

    1. const wrapper = mount(InputComponent);
    2.  
    3. // with no arguments will return a list of classes:
    4. const allClasses = wrapper.classes();
    5. expect(allClasses).toContain('form-input');
    6.  
    7. // with an argument will return boolean:
    8. const hasFormInputClass = wrapper.classes('form-input');
    9. expect(hasFormInputClass).toBe(true);
    Check if a wrapper contains an element or component with contains()

    You can use contains() to check if a Wrapper object contains an element or component matching the selector.

    1. import TextInput from './TextInput.vue';
    2. const wrapper = mount(ContactForm);
    3.  
    4. expect(wrapper.contains('label')).toBe(true);
    5. expect(wrapper.contains(TextInput)).toBe(true);
    Use exists() on a Wrapper or WrapperArray

    If you need to check if a Wrapper is empty you can use exists().

    1. const wrapper = mount(ContactForm);
    2.  
    3. expect(wrapper.exists()).toBe(true);
    4.  
    5. const buttons = wrapper.findAll('button');
    6. const specificButton = wrapper.find('button.submit-button');
    7.  
    8. expect(buttons.exists()).toBe(true); // non empty WrapperArray
    9. expect(specificButton.exists()).toBe(true);
    Do not use find() to search for a component! Use findComponent() instead

    Although it currently works, using find() to find a component is deprecated so should not be used. Instead use findComponent() instead.

    1. import InputForm from './InputForm.vue';
    2.  
    3. const wrapper = mount(ContactForm);
    4.  
    5. // Do NOT do the following:
    6. const deprecated = wrapper.find(InputForm);
    7.  
    8. // Instead you should:
    9. const correct = wrapper.findComponent(InputForm);

    There is also findAllComponents() which returns a WrapperArray object.

    You can chain find() calls

    The following chained code is perfectly valid:

    1. const button = wrapper.find('[data-name="MyButton"]');
    2.  
    3. expect(button.find('.icon').exists()).toBe(true);
    4.  
    5. // another example
    6. // (could be done as a single selector in this case: 'form button')
    7. const anotherExample = wrapper.find('form').find('button');
    Use get() instead of find() when it must find something

    find() can return an empty result. If you need to ensure it finds something (or the test fails) you can use get()

    1. const wrapper = mount(SomeComponent);
    2. const button = wrapper.get('button');
    Do not use these deprecated functions on Wrapper objects

    The following methods are deprecated on Wrapper objects.

    • isEmpty()
    • isVisible()
    • isVueInstance()
    • name()
    • overview()
    • setMethods()
    Use these helper methods to set data on Wrapper or WrapperArray objects

    There are a few setting methods which work on Wrapper or WrapperArray objects. If applied to a WrapperArray it will set it on all items.

    How to use setChecked() in Vue Test Utils

    setChecked() to set checked value for checkbox or radio input elements, and it automatically updates v-model bound data.

    1. const wrapper = mount(SomeComponent);
    2. const radioInput = wrapper.find('input[type="radio"]');
    3. radioInput.setChecked();
    4.  
    5. const checkboxInputs = wrapper.findAll('input[type="checkbox"]');
    6. checkboxInputs.setChecked();
    Using setSelected() in Vue Test Utils

    setSelected() to set an <option> as selected in Vue Test Utils and update the bound v-model data.

    1. const wrapper = mount(SomeComponent);
    2. const options = wrapper.find('select').findAll('option');
    3.  
    4. options.at(2).setSelected();
    How to use setValue() in Vue Test Utils

    Use setValue() on an text control input or select element to set that element's current value, and update the v-model bound data.

    1. const wrapper = mount(SomeComponent);
    2.  
    3. const textInput = wrapper.find('input[type="text"]');
    4. textInput.setValue('new value');
    5.  
    6. const select = wrapper.find('select');
    7. select.setValue('option val');
    Using setData() in Vue Test Utils

    setData() to set the wrapper's VM data (the reactive data in a Vue component).

    Use it by sending an object as the argument.

    1. const wrapper = mount(SomeComponent);
    2. wrapper.setData({foo: 'bar'});
    3. expect(wrapper.vm.foo).toBe('bar');
    Using setProps() in Vue Test Utils

    setProps() to set the wrapper's props. This works in a similar fashion as setData().

    There is also setMethods() but this is deprecated (but does currently work.)

    You can call filter() on WrapperArray objects

    If you use findAll() (which returns a WrapperArray object) you can use the filter() method to filter those items.

    This works in a very similar way to the normal Array.filter() method - pass it a predicate function which returns true or false.

    1. const wrapper = mount(SomeComponent);
    2.  
    3. const allDivs = wrapper.findAll('div');
    4.  
    5. const divsWithActiveClass = allDivs.filter(
    6.        wrapper => wrapper.classes('active')
    7.     );
    8.  
    9. const divsWithCorrectText = allDivs.filter(
    10.        wrapper => wrapper.text() === 'foo'
    11.     );
    12.  
    13. expect(divsWithCorrectText).toHaveLength(3);
    Use is() on WrapperArray objects to check that every wrapper inside it matches the selector

    Sometimes you may want to check that every Wrapper inside a WrapperArray is a certain type.

    1. const wrapper = mount(SomeComponent);
    2.  
    3. const allActiveElements = wrapper.findAll('.active');
    4.  
    5. expect(allActiveElements.is('button')).toBe(true);
    Vue mounting options: data gets merged with existing data

    When mounting with the data config, it will merge with the existing data function.

    1.     const TheComponent = {
    2.     template: '<div>{{ foo }}///{{ bar }}</div>',
    3.  
    4.     data() {
    5.         // The 'existing' data from the component:
    6.         return {
    7.             foo: 'COMPONENT_FOO',
    8.             bar: 'COMPONENT_BAR'
    9.         }
    10.     }
    11.  
    12.     ///////
    13.  
    14.     const wrapper = mount(TheComponent, {
    15.         // The additional data which will merge/overwrite with the
    16.         // existing data in the component.
    17.         data(){
    18.             return {
    19.                 bar: 'FROM_MOUNT_DATA'
    20.             }
    21.         }
    22.     });
    23.  
    24.     // expect the 'foo' value to be from the component,
    25.     // but the 'bar' value should be what we provided in
    26.     // the call to mount()
    27.     expect(wrapper.text()).toBe('COMPONENT_FOO///FROM_MOUNT_DATA')
    Vue mounting options: supplying a string stubs value is deprecated

    It is still very common to stub a component out by providing a string for the stub value:

    1. ...
    2.     stubs: { SomeComponent: '<div class="stubbed" />'}
    3. ...

    This is commonly used but it is deprecated and should not be done.

    The only non-deprecated values for the stubs object is a component (object) or boolean. (However, strings are still working in the current version of Vue Test Utils).

    Here are some examples of how to stub out a component in Vue Test Utils:

    1. import AValidComponent from '../AValidComponent.vue';
    2.  
    3. const wrapper = mount(Component, {
    4.    stubs: {
    5.       // The following line is deprecated but does work:
    6.       SomeChildComponent: '<div class="stubbed" />'
    7.  
    8.       // the following 4 lines are all valid:
    9.       AnotherComponent: false,
    10.       DifferentComponent: false,
    11.       StubbedComponent: true,
    12.       StubbedByComponent: AValidComponent,
    13. }
    14.     });
    Vue mounting options: propsData - Use propsData mounting option to set initial props

    This is probably well known, but to set props in Vue Test Utils you can set propsData.

    1. const wrapper = mount(Component, {
    2.     propsData: {
    3.         somePropName: 'the-value',
    4.         anotherProp: true,
    5.     }
    6. }
    7. });

    This is actually just using the normal Vue functionality - you can use propsData in your non test code too.

    Vue mounting options: use the listeners attribute to set a component instance's $listeners object

    Another trick that is probably well known, but you can set the listeners in the mount options.

    1. const onClick = jest.fn();
    2. const wrapper = mount(ComponentWhichUsesListeners, {
    3.    listeners: { click: onClick}
    4. }
    5.  
    6. wrapper.trigger('click');
    7.  
    8. expect(onClick).toHaveBeenCalled();
    9. });
    Vue mounting options: use the parentComponent option to set the component to use as a parent.

    If you need to set a parent component when mounting a (child) component, you can set it with the parentComponent option.

    1. import Something from '../Something.vue';
    2.  
    3. const wrapper = mount(ComponentWhichUsesListeners, {
    4.    parentComponent: Something
    5. }
    6.  
    7. // You can access it with wrapper.vm.$parent
    8. });

    Subscribe to my spam free newsletter for other Laravel and Vue updates like this

    I never spam, and only email when I have a good in-depth post published on my site (mostly about Laravel and Vue). You can also follow me on social media to get updates.

    webdevetc profile pic
    webdevetc

    I've been working as a software developer for many years (with the last few years specialising in Laravel and Vue). I mostly write about PHP and JavaScript/TypeScript on this site. Contact me here. Need to hire a contract software developer in London, UK (or freelance)? Contact me and check my availability.

    Leave a Comment