Unit Test Vue Apps with Vue Test Utils — Mock External Dependencies
Tags: webdev, javascript, testing, vue
Published: 2021-01-23 02:11:18

**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.

### Test Async Behavior Outside of Vue

To test async behavior outside of Vue, we have to mock the async code before we run our test.

For example, if we have a `YesNo.vue` component in the `components` folder:

```
<template>
  <div>
    <button @click="fetchResults">fetch answer</button>
    <p>{{ answer }}</p>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: "YesNo",
  data() {
    return {
      answer: undefined
    }
  },
  methods: {
    async fetchResults() {
      const { data: { answer } } = await axios.get('https://yesno.wtf/api')
      this.answer = answer;
    }
  }
};
</script>
```

We can test it by mocking the `axios` object and put our own `get` method in the mocked object:

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

jest.mock('axios', () => ({
  get: () => Promise.resolve({ data: { answer: 'yes' } })
}))

describe('YesNo tests', () => {
  it('renders answer after clicking fetch answer', async () => {
    const wrapper = shallowMount(YesNo)
    await wrapper.find('button').trigger('click')
    expect(wrapper.text()).toContain('yes')
  })

})
```

Since `axios.get` returns a promise with the response, we do the same thing with our mock `get` method.

Then when we click the button, we should see the `'yes'` answer.

### Using with Vue Router

We can test our Vue app with Vue Router.

For example, given that we have the following Vue app code:

`components/Bar.vue`

```
<template>
  <div>Bar</div>
</template>

<script>
export default {
  name: "Bar",
};
</script>
```

`components/Foo.vue`

```
<template>
  <div>Foo</div>
</template>

<script>
export default {
  name: "Foo",
};
</script>
```

`main.js`

```
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import Foo from '@/components/Foo';
import Bar from '@/components/Bar';

const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]

const router = new VueRouter({
  routes
})

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')
```

We can test the `Foo` component by writing:

```
import { shallowMount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
import Foo from '@/components/Foo';

const localVue = createLocalVue()
localVue.use(VueRouter)
const router = new VueRouter()

describe('Foo tests', () => {
  it('renders the Foo component', async () => {
    const wrapper = shallowMount(Foo, {
      localVue,
      router
    })
    expect(wrapper.text()).toContain('Foo')
  })

})
```

We create a local `Vue` instance with the `createLocalVue` function.

Then we call `use` to add the `VueRouter` plugin.

Also, we create a new `VueRouter` instance.

Then we pass that all in when we mount our component so that we can test it in isolation.

### Testing components that use `router-link` or `router-view`

There are a few ways to test components that use the `router-link` or `router-view` components.

One way is to stub those components by putting the component names in the `stubs` array:

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

describe('App tests', () => {
  it('renders the App component', async () => {
    const wrapper = shallowMount(App, {
      stubs: ['router-link', 'router-view']
    })
    expect(wrapper.find('app')).toBeTruthy()
  })

})
```

We mount our `App` component from the previous example code with the stub for the `router-link` and `router-view` components.

Then we can carry on with our tests.

We can also pass in the `localVue` object with the local version of the `Vue` instance that has the `VueRouter` instance added to it:

```
import { shallowMount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
import App from '@/App';

const localVue = createLocalVue()
localVue.use(VueRouter)

describe('App tests', () => {
  it('renders the App component', async () => {
    const wrapper = shallowMount(App, {
      localVue
    })
    expect(wrapper.find('app')).toBeTruthy()
  })

})
```

### Conclusion

We can test our app with external dependencies by mocking them so that they can run reliably without relying on external resources.