DEV Community

Albert Mulia Shintra
Albert Mulia Shintra

Posted on • Edited on

Vue Test Utils v1 finally released! More reliable, better testing practice, and deprecation warning

Heya!

Before I start, I'll share my short background on why this release means a lot to me :) Feel free to skip to the detail section below (More Reliable)

As part of the Frontend team in Piktochart, we have been using VueJS since 2 years ago and it was a blast to migrate from native JS to a framework that helps with code organizing and better practices. Naturally, this comes along with the automated test too!

We have been using Jest and VueTestUtils since the first time we migrate to Vue, and we rely on it to test the Vue components. We've tried our best to follow the recommended tips, which preferably uses shallowMount, simulate the component's data input (ex: props, network response, user interaction) and assert the output (ex: rendered HTML, emitted events).

Yet, sometimes we faces issue in the testing process and we need to have a workaround to ensure the test can simulate and output the test scenario. We understand that it's because the VueTestUtils has been in a beta state since late 2017, so it's still prone to bugs and API changes. Let me know by commenting below if you face some similar issues.

Which is why, when they finally release the stable version, we can be sure that the test is going to be better.

Let's go through the release in detail below! :D

More Reliable

The stable release has tried its best to fix the known problems of the test framework when it's in the beta phase. Here are some of the bugs that I've faced before, and fixed in the latest release:

Fix on shallowMount, cannot test child component's slot

Link to the reported issue: 1, 2, 3

When a component uses the child component's slot to render the content, we need to test the interactivity of the content from the slot as well. It was working fine if the slot is a default slot, but it isn't for named slot. Take a look at this example (using b-modal from bootstrap-vue):

// parent component
<template>
  <b-modal v-model="visible" static>
    <template v-slot:modal-header>
      Header Title
    </template>
    Modal Content
  </b-modal>
</template>
Enter fullscreen mode Exit fullscreen mode

note: the b-modal has static props, so the modal is rendered inside the parent component

We would like to test if the modal header renders the correct title:

const wrapper = shallowMount(parentComponent);
const modalWrapper = wrapper.find({ name: 'BModal' });
expect(wrapper.html()).toContain('Header Title');
Enter fullscreen mode Exit fullscreen mode

In the test, the rendered HTML when the visible: true is:

// wrapper.html()
<b-modal-stub size="md" ignoreenforcefocusselector="" title="" titletag="h5" headerclosecontent="&amp;times;" headercloselabel="Close" canceltitle="Cancel" oktitle="OK" cancelvariant="secondary" okvariant="primary" static="true" visible="true">
  Modal Content
</b-modal-stub>
Enter fullscreen mode Exit fullscreen mode

Notice that the default slot ("Modal Content") is rendered, but the named slot header ("Header Title") is missing 😧. Because of this, we cannot test the content rendered in the header.

The workaround is to stub the child component to the real component, so it'll render the default and named slot.

import { BModal } from 'bootstrap-vue'

const wrapper = shallowMount(parentComponent, {
  stubs: {
    BModal
  }
});
Enter fullscreen mode Exit fullscreen mode

This test has been failing in the < v1.0.0-beta.27, and finally passed in the v1.0.0!

Although it might not be a proper fix, the Core team is looking into it and they're looking for help too if you are interested 😉.

Fix on shallowMount, cannot assert child component's v-model

This is the reported issue: 1

When a component that binds the data to the child component using v-model, it wasn't testable before because the props aren't detected.

This is the test example:

// parent component
<template>
  <input-link v-model="url" />
</template>
<script>
export default {
  data () {
    return { url: '' }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

To ensure the parent component has passed the right data to the child component (<input-link>), we need to test it:

it('gets data binding of `url` to <input-link> component', async () => {
  const newUrl = 'https://chenxeed.com';
  const wrapper = shallowMount(HelloWorld);
  await wrapper.setData({ url: newUrl }); // await to change the child props
  const inputLink = wrapper.find({ name: 'InputLink' });
  expect(inputLink.props('url')).toBe(newUrl);
})
Enter fullscreen mode Exit fullscreen mode

This test has been failing in the v1.0.0-beta.25, and passed in the v1.0.0!

Fix on shallowMount, cannot assert child component loaded by dynamic import

This is the reported issue: 1, 2

When a component that loads the child component by using dynamic import, it wasn't testable before because the child aren't rendered properly.

This is the test example:

<template>
  <child-component/>
</template>
<script>
export default {
  components: {
    ChildComponent: () => import('./child-component')
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

We need to assert if the child component is loaded:

const wrapper = shallowMount(parentComponent);
const childComponent = wrapper.find({ name: 'ChildComponent' });
expect(childComponent.exists()).toBeTruthy();
Enter fullscreen mode Exit fullscreen mode

This test is not working, and there's a fix on v1.0.0-beta.28 to render the dynamic import component in shallowMount but you have to run nextTick first. The quirk is, the rendered component is not stubbed 😳.

const wrapper = shallowMount(parentComponent);
await wrapper.vm.$nextTick(); // must await nextTick to render the child
const childComponent = wrapper.find({ name: 'ChildComponent' });
expect(childComponent.exists()).toBeTruthy();
Enter fullscreen mode Exit fullscreen mode

Although it might not be a proper fix, the Core team is looking into it and they're looking for help too if you are interested 😉.

There's more changes that I can't cover all here, so apologies if I missed to highlight any issues that you have faced before 🙇 You can check the releases for the fixes they have made.

Now let's highlight the better practice introduced in the stable release! 💖

Better testing practice

The stable release has introduced a better practice on handling user event like mouse click, and any events that changes the component data.

Previously, we need to manually run a nextTick whenever we trigger the event or changing some data:

wrapper.setData({
  url: 'newurl'
});
await wrapper.vm.$nextTick();
expect(wrapper.html()).toContain('newurl');
Enter fullscreen mode Exit fullscreen mode

Now, we can shortcut this by awaiting the setter function:

await wrapper.setData({
  url: 'newurl'
});
expect(wrapper.html()).toContain('newurl');
Enter fullscreen mode Exit fullscreen mode

Isn't it lovely? 😍 Same goes for the event trigger like click:

await wrapper.trigger('click');
expect(wrapper.emitted().clicked).toBeTruthy();
Enter fullscreen mode Exit fullscreen mode

Deprecation warning

The stable release also introduce some warnings to deprecate the usage of some API that they find not necessary, or can be replaced. Here are the APIs to be deprecated:

  • attachToDocument is deprecated and will be removed in future releases. Use attachTo instead.
  • isEmpty is deprecated and will be removed in future releases.
  • isVueInstance is deprecated and will be removed in future releases.
  • setMethods is deprecated and will be removed in future releases.
  • emittedByOrder is deprecated and will be removed in future releases.
  • Using find to search for a Component is deprecated and will be removed. Use findComponent instead.
  • Using findAll to search for Components is deprecated and will be removed. Use findAllComponents instead.
  • isVisible is deprecated and will be removed in future releases.
  • isVueInstance is deprecated and will be removed in future releases.
  • name is deprecated and will be removed in future releases.
  • overview is deprecated and will be removed in future releases.

note: you can disable the warnings if it annoys your testing. Check the release log for more detail

These API are likely to be removed on the next version for the Vue 3 support, so keep in mind as it'll likely break your existing tests if you are migrating your Vue 2 app to Vue 3!

Summary

The VueJS Developer has been going through some roller coaster on testing their components with the issues reported, and this release is a green light to let the developer be more confident on writing the unit tests.

Really appreciate the effort of the VueTestUtils Core team to release the stable version, while preparing for the upcoming Vue 3 test 😍

I hope this article helps you to look forward on the Vue Test Utils upgrade, and let me know in the comment if anything is unclear. Enjoy testing!

happy-testing

Top comments (0)