Categories
Vue Testing

Unit Test Vue Apps with Vue Test Utils — Lifecycle and Async Tests

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.

Lifecycle Hooks

When we call mount or shallowMount to mount our components, the lifecycle methods will be run.

However, the beforeDestroy or destroyed methods won’t be triggered unless the component is manually destroyed with wrapper.destroy() .

The component won’t be automatically destroyed at the end of each test, so we have to clean up any dependencies manually.

Writing Asynchronous Tests

When we write tests, we have to be aware that any changes to reactive properties are done asynchronously.

This means that we have to the test as an async function or use then and run our test code that runs after the reactive property is updated in the then callback.

For example, we can write:

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

const Counter = {
  template: `
    <div>
      <button @click="count++">Add up</button>
      <p>clicks: {{ count }}</p>
    </div>
  `,
  data() {
    return { count: 0 }
  }
}

describe('Counter', () => {
  it('renders new number when Add up button is clicked', async () => {
    const wrapper = shallowMount(Counter)
    const button = wrapper.find('button')
    const text = wrapper.find('p')
    await button.trigger('click')
    expect(text.text()).toBe('clicks: 1')
  })
})

to await the trigger method so that we can check the updated value of the count reactive property after the update is done.

We can also write:

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

const Counter = {
  template: `
    <div>
      <button @click="count++">Add up</button>
      <p>clicks: {{ count }}</p>
    </div>
  `,
  data() {
    return { count: 0 }
  }
}

describe('Counter', () => {
  it('renders new number when Add up button is clicked', () => {
    const wrapper = shallowMount(Counter)
    const button = wrapper.find('button')
    const text = wrapper.find('p')
    button.trigger('click')
      .then(() => {
        expect(text.text()).toBe('clicks: 1')
      })
  })
})

which is the same code without async and await.

Asserting Emitted Events

We can check which events are emitted when we do something with the component.

To do this, we can call the emitted method to check which events are emitted.

For instance, we can write:

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

const Foo = {
  template: `
    <div>
      <button @click="$emit('foo')">emit foo</button>
    </div>
  `,
}

describe('Foo', () => {
  it('emits foo event when the emit foo button is clicked', async () => {
    const wrapper = shallowMount(Foo)
    const button = wrapper.find('button');
    await button.trigger('click')
    expect(wrapper.emitted().foo).toBeTruthy()
  })
})

We have the Foo component that calls $emit to emit the 'foo' event when we click it.

Then in the last line of the test, we call wrapper.emitted() to check if the foo event is emitted.

If it’s emitted, then the foo property should be truthy.

We can also check which payload is emitted. For example, we can write:

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

const Foo = {
  template: `
    <div>
      <button @click="$emit('foo', 123)">emit foo</button>
    </div>
  `,
}

describe('Foo', () => {
  it('emits foo event with payload 123 when the emit foo button is clicked', async () => {
    const wrapper = shallowMount(Foo)
    const button = wrapper.find('button');
    await button.trigger('click')
    expect(wrapper.emitted().foo.length).toBe(1)
    expect(wrapper.emitted().foo[0]).toEqual([123])
  })
})

We check the foo property’s length to see how many times it’s emitted.

And the payload is checked by accessing foo ‘s array value and checking the content.

Conclusion

We can check if events are emitted and be aware that lifecycle hooks like destroy aren’t run automatically.

Categories
Vue Testing

Unit Test Vue Apps with Vue Test Utils

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.

Installation

We can add it when we run Vue CLI to scaffold our app.

We select Unit Tests from the list and then select Jest to add the test framework with the production code.

Usage

Once we added the test files, we can write our unit tests.

In our unit tests, we can interact with our components like we do when using them.

To do that, we mount the component and interact with the elements by triggering events on them.

Also, we can mock any data and external resources that need to be set to let us do the tests.

This way, we can run our tests independently without relying on anything external that makes the tests unreliable.

To write our first test, we can add the test in the tests/unit folder by writing:

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

const MessageComponent = {
  template: '<p>{{ msg }}</p>',
  props: ['msg']
}

describe('MessageComponent', () => {
  it('renders props.msg when passed', () => {
    const msg = 'new message'
    const wrapper = shallowMount(MessageComponent, {
      propsData: { msg }
    })
    expect(wrapper.text()).toMatch(msg)
  })
})

We have the MessageComponent that renders the value of the msg prop onto the screen.

In our test, which is in the callback we pass into it , we call shallowMount to mount the component without its children.

propsData lets us pass the prop data we need for the test.

Then the wrapper.text() method returns the rendered text.

And we use toMatch to check the results.

To run the test, we run npm run test:unit to run the unit test and then we should see some green text to indicate that the test passed.

Simulating User Interaction

We can also simulate user interaction easily.

To do that, we can write:

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

const Counter = {
  template: `
    <div>
      <button @click="count++">Add up</button>
      <p>clicks: {{ count }}</p>
    </div>
  `,
  data() {
    return { count: 0 }
  }
}

describe('Counter', () => {
  it('renders new number when Add up button is clicked', async () => {
    const wrapper = shallowMount(Counter)
    const button = wrapper.find('button')
    const text = wrapper.find('p')
    await button.trigger('click')
    expect(text.text()).toBe('clicks: 1')
  })
})

We have the Counter component that we want to test.

It has a button that increases count by 1 when we click it.

Then in the test, we call shallowMount to mount the component.

Then we call wrapper.find to get the button and the p element.

To simulate a click on the button, we call trigger to trigger the click event.

trigger is async is returns a promise, so our test callback has to be an async function and we have to await the trigger method.

Then we check the text displayed in the p element with the text method and see if the text has been updated.

Conclusion

We can write unit tests easily to test Vue apps by using Jest and simulating user actions.

Categories
Vue Testing

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

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')
  })
})

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)
  })
})

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')
  })
})

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')
  })
})

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.