Unit Test Vue Apps with Vue Test Utils — Child Components and Reactive Properties

John Au-Yeung - Jan 23 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

With the Vue Test Utils library, we can write and run unit tests for Vue apps easily.

In this article, we’ll look at how to write unit tests with the Vue Test Utils library.

Emitting Event from Child Component

We can check for events that are emitted in a child component.

To do that, we call mount to mount the parent component with the child component.

Then we can emit the event we want on the child:

import { mount } from '@vue/test-utils'

const ChildComponent = {
  template: `
    <div>
      <p>child</p>
    </div>
  `,
}

const ParentComponent = {
  template: `
    <div>
      <child-component @custom="onCustom" />
      <p id='emitted' v-if="emitted">emitted</p>
    </div>
  `,
  data() {
    return { emitted: false }
  },
  methods: {
    onCustom() {
      this.emitted = true
    }
  },
  components: {
    ChildComponent
  }
}

describe('ParentComponent', () => {
  it('shows emitted when child-component emits custom event', async () => {
    const wrapper = mount(ParentComponent)
    await wrapper.findComponent(ChildComponent).vm.$emit('custom')
    expect(wrapper.find('#emitted').text()).toContain('emitted')
  })
})
Enter fullscreen mode Exit fullscreen mode

To mount the child component of ParentComponent with the ParentComponent , we call the mount method.

The findComponent method has ChildComponent as the argument so that we can use the returned component wrapper to emit the custom event with the $emit method.

Then to check if the ‘emitted’ text is displayed, we call the find method to check if it has the 'emitted' string.

Manipulating Component State

We can manipulate component state with the setData or setProps method.

For example, we can write:

import { mount } from '@vue/test-utils'

const Counter = {
  template: `
    <div>
      <p>{{count}}</p>
    </div>
  `,
  data() {
    return { count: 0 }
  },
}

describe('Counter', () => {
  it('renders 10 for count', async () => {
    const wrapper = mount(Counter)
    await wrapper.setData({ count: 10 })
    expect(wrapper.find('p').text()).toContain(10)
  })
})
Enter fullscreen mode Exit fullscreen mode

to call the setData method with the data we want to set.

The key is the key of a reactive property and the value is its value.

To set props, we can use the setProps method:

import { mount } from '@vue/test-utils'

const Foo = {
  template: `
    <div>
      <p>{{foo}}</p>
    </div>
  `,
  props: ['foo']
}

describe('Foo', () => {
  it('renders bar for foo prop', async () => {
    const wrapper = mount(Foo)
    await wrapper.setProps({ foo: 'bar' })
    expect(wrapper.find('p').text()).toContain('bar')
  })
})
Enter fullscreen mode Exit fullscreen mode

We call the setProps method to set the value of the foo prop to 'bar' .

Then we check that if that is rendered in the last line of the test.

Props can also be set when we mount the component:

import { mount } from '@vue/test-utils'

const Foo = {
  template: `
    <div>
      <p>{{foo}}</p>
    </div>
  `,
  props: ['foo']
}

describe('Foo', () => {
  it('renders bar for foo prop', async () => {
    const wrapper = mount(Foo, {
      propsData: {
        foo: 'bar'
      }
    })
    expect(wrapper.find('p').text()).toContain('bar')
  })
})
Enter fullscreen mode Exit fullscreen mode

The 2nd argument of the mount method takes the propsData property to let us mock the props.

Conclusion

We can mock props and set values of reactive properties in our tests and test them.

Child component events can also be tested from the parent.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .