Testing Vue 3 Apps — Testing Event Handling

John Au-Yeung - Jan 19 '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 apps getting more complex than ever, it’s important to test them automatically. We can do this with unit tests, and then we don’t have to test everything by hand.

In this article, we’ll look at how to test Vue 3 apps by writing a simple app and testing it.

Event Handling

We can test event handling in our Vue 3 components easily with Vue Test Utils.

For example, we can write:

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

const Counter = {
  template: '<button @click="handleClick">Increment</button>',
  data() {
    return {
      count: 0
    }
  },
  methods: {
    handleClick() {
      this.count += 1
      this.$emit('increment', this.count)
    }
  }
}

test('emits increment event when button is clicked', () => {
  const wrapper = mount(Counter)
  wrapper.find('button').trigger('click')
  expect(wrapper.emitted()).toHaveProperty('increment')
})
Enter fullscreen mode Exit fullscreen mode

We have the Counter component that has a button that runs the handleclick method when we click it.

The handleClick method emits the increment event.

In the test, we mount the Counter component.

Then we get the button with find and call trigger with the click method.

And then we check if the increment event is emitted with the wrapper.emitted method.

The returned object has the event names as the key.

We can do other checks in our tests with regard to events.

For instance, we can write:

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

const Counter = {
  template: '<button @click="handleClick">Increment</button>',
  data() {
    return {
      count: 0
    }
  },
  methods: {
    handleClick() {
      this.count += 1
      this.$emit('increment', this.count)
    }
  }
}

test('emits increment event when button is clicked', () => {
  const wrapper = mount(Counter)
  wrapper.find('button').trigger('click')
  expect(wrapper.emitted()).toHaveProperty('increment')
  const incrementEvent = wrapper.emitted('increment')
  expect(incrementEvent).toHaveLength(1)
  expect(incrementEvent[0]).toEqual([1])

})
Enter fullscreen mode Exit fullscreen mode

We get the increment event object with:

const incrementEvent = wrapper.emitted('increment')
Enter fullscreen mode Exit fullscreen mode

Then we check how many time it’s emitted with:

expect(incrementEvent).toHaveLength(1)
Enter fullscreen mode Exit fullscreen mode

And we check the value that it’s emitted with, which the 2nd argument of this.$emit with:

expect(incrementEvent[0]).toEqual([1])
Enter fullscreen mode Exit fullscreen mode

Asserting Complex Events

We can test more complex event objects.

For example, we can write:

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

const Counter = {
  template: '<button @click="handleClick">Increment</button>',
  data() {
    return {
      count: 0
    }
  },
  methods: {
    handleClick() {
      this.count += 1
      this.$emit('increment', {
        count: this.count,
        isEven: this.count % 2 === 0
      })
    }
  }
}

test('emits increment event when button is clicked', () => {
  const wrapper = mount(Counter)
  wrapper.find('button').trigger('click')
  wrapper.find('button').trigger('click')
  expect(wrapper.emitted('increment')).toHaveLength(2)
  const [event1, event2] = wrapper.emitted('increment')
  expect(event1).toEqual([
    {
      count: 1,
      isEven: false
    }
  ])
  expect(event2).toEqual([
    {
      count: 2,
      isEven: true
    }
  ])
})
Enter fullscreen mode Exit fullscreen mode

We trigger the click event on the button twice.

Then we check the number of times the increment event is emitted with:

expect(wrapper.emitted('increment')).toHaveLength(2)
Enter fullscreen mode Exit fullscreen mode

Then we get the event objects that are emitted with:

const [event1, event2] = wrapper.emitted('increment')
Enter fullscreen mode Exit fullscreen mode

Then we check the event object that we emitted with the event with:

expect(event1).toEqual([{
  count: 1,
  isEven: false
}])
expect(event2).toEqual([{
  count: 2,
  isEven: true
}])
Enter fullscreen mode Exit fullscreen mode

Conclusion

We can test event emissions with Vue 3 and Vue Test Utils.

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