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

Table of contents

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.

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:

function someTest(done) {
    // Some code setup here.
    Vue.nextTick(function testSomethingAfterNextTick() {
        expect(something).toBe(false);
        done();
    });
}

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

async function someTest() {
    // Some code setup here.
    await Vue.nextTick();
    expect(something).toBe(false);
}

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():

async function someTest() {
    wrapper = mount(Component);
    wrapper.trigger('click');
    await Vue.nextTick();
    expect(something).toBe(true);
}

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:

async function someTest() {
    wrapper = mount(Component);
    await wrapper.trigger('click');
    expect(something).toBe(true);
}

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:

// this will not be caught
it('will time out', done => {
    Vue.nextTick(() => {
        expect(true).toBe(false);
        done();
    });
});
// the three following tests will work as expected
it('will catch the error using done', done => {
    Vue.config.errorHandler = done;
    Vue.nextTick(() => {
        expect(true).toBe(false);
        done();
    });
});
it('will catch the error using a promise', () => {
    return Vue.nextTick().then(function() {
        expect(true).toBe(false);
    });
});
it('will catch the error using async/await', async () => {
    await Vue.nextTick();
    expect(true).toBe(false);
});

Get emitted events from a component

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

// Test the 'someEventName' was fired 3 times:
expect(wrapper.emitted().someEventName.length).toBe(3);
// Test the payload for an event:
const expectedPayload = '...';
expect(wrapper.emitted().yourEvent[0]).toEqual(expectedPayload);
// Test something was not emitted:
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:

import { shallowMount } from '@vue/test-utils'
import ParentComponent from '../components/ParentComponent'
import ChildComponent from '../components/ChildComponent'
describe('ParentComponent', () => {
    it("displays 'Emitted!' when custom event is emitted", () => {
        const wrapper = shallowMount(ParentComponent);
        // next line acts as if the child component emitted 'custom'
        wrapper.find(ChildComponent).vm.$emit('custom');
        expect(wrapper.html()).toContain('Emitted!');
    });
})

Get all listeners for a component in Vue Test Utils

Get all listeners with the vm.$listeners attribute.

const wrapper = mount(ParentComponent);
const listeners = wrapper.vm.$listeners;
const childComponentListeners = wrapper.find(ChildComponent).vm.$listeners;

Stub out transitions with Vue Test Utils

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.

// stub function which will just render the children:
const transitionStub = () => ({
    render: function(h) {
        return this.$options._renderChildren
    }
})
test('should render Foo, then hide it', async () => {
    const wrapper = mount(Foo, {
        stubs: {
            // Stub out the transition:
            transition: transitionStub()
        }
    })
    expect(wrapper.text()).toMatch(/Foo/)
    wrapper.setData({
        show: false
    })
    await wrapper.vm.$nextTick()
    expect(wrapper.text()).not.toMatch(/Foo/)
})

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:

import { shallowMount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
const localVue = createLocalVue()
localVue.use(VueRouter)
shallowMount(Component, {
    localVue
})

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!

import { shallowMount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
const localVue = createLocalVue()
localVue.use(VueRouter)
const $route = {
    path: '/some/path'
}
const wrapper = shallowMount(Component, {
    localVue,
    mocks: {
        // $route does not work! It was set to read only when using localVue.
        $route
    }
})

Get all Vue props with wrapper.props()

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

 const wrapper = mount(SomeComponent, {
    propsData: {
      name: 'webdevetc',
    }
});
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.

const wrapper = await render(SomeComponent);
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.

const wrapper = mount(SomeComponent);
expect(wrapper.is(SomeComponent)).toBe(true);
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.

const wrapper = mount(SomeComponent);
const btn = wrapper.find({ ref: 'myButton'});
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.

import { createWrapper } from '@vue/test-utils'
import Foo from './Foo.vue'
const Constructor = Vue.extend(Foo)
const vm = new Constructor().$mount()
const wrapper = createWrapper(vm)
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.

import { config } from '@vue/test-utils';
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.)

const wrapper = mount(Component);
const link = wrapper.find('li > a');
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.

const wrapper = mount(InputComponent);
expect(wrapper.attributes().placeholder).toBe('Enter your email');
// use with argument:
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.

const wrapper = mount(InputComponent);
// with no arguments will return a list of classes:
const allClasses = wrapper.classes();
expect(allClasses).toContain('form-input');
// with an argument will return boolean:
const hasFormInputClass = wrapper.classes('form-input');
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.

import TextInput from './TextInput.vue';
const wrapper = mount(ContactForm);
expect(wrapper.contains('label')).toBe(true);
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().

const wrapper = mount(ContactForm);
expect(wrapper.exists()).toBe(true);
const buttons = wrapper.findAll('button');
const specificButton = wrapper.find('button.submit-button');
expect(buttons.exists()).toBe(true); // non empty WrapperArray
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.

import InputForm from './InputForm.vue';
const wrapper = mount(ContactForm);
// Do NOT do the following:
const deprecated = wrapper.find(InputForm);
// Instead you should:
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:

const button = wrapper.find('[data-name="MyButton"]');
expect(button.find('.icon').exists()).toBe(true);
// another example
// (could be done as a single selector in this case: 'form button')
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()

const wrapper = mount(SomeComponent);
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.

const wrapper = mount(SomeComponent);
const radioInput = wrapper.find('input[type="radio"]');
radioInput.setChecked();
const checkboxInputs = wrapper.findAll('input[type="checkbox"]');
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.

const wrapper = mount(SomeComponent);
const options = wrapper.find('select').findAll('option');
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.

const wrapper = mount(SomeComponent);
const textInput = wrapper.find('input[type="text"]');
textInput.setValue('new value');
const select = wrapper.find('select');
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.

const wrapper = mount(SomeComponent);
wrapper.setData({foo: 'bar'});
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.

const wrapper = mount(SomeComponent);
const allDivs = wrapper.findAll('div');
const divsWithActiveClass = allDivs.filter(
       wrapper => wrapper.classes('active')
    );
const divsWithCorrectText = allDivs.filter(
       wrapper => wrapper.text() === 'foo'
    );
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.

const wrapper = mount(SomeComponent);
const allActiveElements = wrapper.findAll('.active');
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.

    const TheComponent = {
    template: '<div>{{ foo }}///{{ bar }}</div>',
    data() {
        // The 'existing' data from the component:
        return {
            foo: 'COMPONENT_FOO',
            bar: 'COMPONENT_BAR'
        }
    }
    ///////
    const wrapper = mount(TheComponent, {
        // The additional data which will merge/overwrite with the
        // existing data in the component.
        data(){
            return {
                bar: 'FROM_MOUNT_DATA'
            }
        }
    });
    // expect the 'foo' value to be from the component,
    // but the 'bar' value should be what we provided in
    // the call to mount()
    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:

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

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:

import AValidComponent from '../AValidComponent.vue';
const wrapper = mount(Component, {
   stubs: {
      // The following line is deprecated but does work:
      SomeChildComponent: '<div class="stubbed" />'
      // the following 4 lines are all valid:
      AnotherComponent: false,
      DifferentComponent: false,
      StubbedComponent: true,
      StubbedByComponent: AValidComponent,
}
    });

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.

const wrapper = mount(Component, {
    propsData: {
        somePropName: 'the-value',
        anotherProp: true,
    }
}
});

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.

const onClick = jest.fn();
const wrapper = mount(ComponentWhichUsesListeners, {
   listeners: { click: onClick}
}
wrapper.trigger('click');
expect(onClick).toHaveBeenCalled();
});

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.

import Something from '../Something.vue';
const wrapper = mount(ComponentWhichUsesListeners, {
   parentComponent: Something
}
// You can access it with wrapper.vm.$parent
});

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