Vue Test Utils v1 finally released! More reliable, better testing practice, and deprecation warning
Tags: javascript, vue, testing, changelog
Published: 2020-05-11 18:43:52

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](http://piktochart.com/), 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](https://jestjs.io/) and [VueTestUtils](https://github.com/vuejs/vue-test-utils) 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](https://vue-test-utils.vuejs.org/guides/common-tips.html), which **preferably uses `shallowMount`**, **simulate the component's data input** (ex: props, network response, user interaction) and **[assert](https://jestjs.io/docs/en/expect) 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](https://github.com/vuejs/vue-test-utils/releases/tag/v1.0.0), 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](https://github.com/vuejs/vue-test-utils/issues/1413), [2](https://github.com/vuejs/vue-test-utils/issues/1307#issuecomment-617503092), [3](https://github.com/vuejs/vue-test-utils/pull/1309)

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](https://bootstrap-vue.org/docs/components/modal) from bootstrap-vue):

```vue
// parent component
<template>
  <b-modal v-model="visible" static>
    <template v-slot:modal-header>
      Header Title
    </template>
    Modal Content
  </b-modal>
</template>
```
_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:

```js
const wrapper = shallowMount(parentComponent);
const modalWrapper = wrapper.find({ name: 'BModal' });
expect(wrapper.html()).toContain('Header Title');
```

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

```html
// 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>
```

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.

```js
import { BModal } from 'bootstrap-vue'

const wrapper = shallowMount(parentComponent, {
  stubs: {
    BModal
  }
});
```

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](https://github.com/vuejs/vue-test-utils/issues/965)

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:

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

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

```js
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);
})
```

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](https://github.com/vuejs/vue-test-utils/issues/882), [2](https://github.com/vuejs/vue-test-utils/issues/1279)

When a component that loads the child component by using [dynamic import](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components), it wasn't testable before because the child aren't rendered properly.

This is the test example:

```vue
<template>
  <child-component/>
</template>
<script>
export default {
  components: {
    ChildComponent: () => import('./child-component')
  }
}
</script>
```

We need to assert if the child component is loaded:

```js
const wrapper = shallowMount(parentComponent);
const childComponent = wrapper.find({ name: 'ChildComponent' });
expect(childComponent.exists()).toBeTruthy();
```

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](https://github.com/vuejs/vue-test-utils/issues/1279) :flushed:.

```js
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();
```

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 :bow: You can check the [releases](https://github.com/vuejs/vue-test-utils/releases) for the fixes they have made.

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

# 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:

```js
wrapper.setData({
  url: 'newurl'
});
await wrapper.vm.$nextTick();
expect(wrapper.html()).toContain('newurl');
```

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

```js
await wrapper.setData({
  url: 'newurl'
});
expect(wrapper.html()).toContain('newurl');
```

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

```js
await wrapper.trigger('click');
expect(wrapper.emitted().clicked).toBeTruthy();
```

# 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](https://github.com/vuejs/vue-test-utils/releases) 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 :heart_eyes:

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](https://mazipan.space/static/bbab1977028f3d445a5446c29ae4dd3c/53025/commitstrip.jpg)