Unit Test Vue Apps with Vue Test Utils — Vue Router and Vuex Mocks
Tags: javascript, webdev, testing, vue
Published: 2021-01-23 02:11:45

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

### Mocking `$route` and `$router`

We can mock the `$route` and `$router` objects to inject the Vue Router’s reactive properties into our mounted component.

For instance, we can write:

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

const Component = {
  template: `
    <div>
      <p>{{$route.path}}</p>
    </div>
  `
}

describe('Component', () => {
  it('renders the Component component with the path', async () => {
    const $route = {
      path: '/some/path'
    }
    const wrapper = shallowMount(Component, {
      mocks: {
        $route
      }
    })
    expect(wrapper.text()).toContain('/some/path')
  })

})
```

We have the `Component` component that renders the `$route.path` value.

We just put that in the `mocks` property so that it’ll be set with the given value when we mount the component.

### Testing Vuex in Components

We can test components that depends on Vuex.

For example, if we have a Vue app that has the following code:

`main.js`

```
import Vue from 'vue'
import App from './App.vue'
import Vuex from 'vuex'

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Vue.config.productionTip = false

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

`App.vue`

```
<template>
  <div id="app">
    <button @click="$store.dispatch('increment')">add</button>
    <p>{{count}}</p>
  </div>
</template>

<script>
export default {
  name: "App",
  computed: {
    count() {
      return this.$store.state.count;
    },
  },
};
</script>
```

Then we can test the code by writing:

`example.spec.js`

```
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import App from '@/App'

const localVue = createLocalVue()

localVue.use(Vuex)

describe('App.vue', () => {
  let actions
  let store

  beforeEach(() => {
    actions = {
      increment: jest.fn(),
    }
    store = new Vuex.Store({
      actions
    })
  })

  it('dispatches "increment" action when button is clicked', async () => {
    const wrapper = shallowMount(App, { store, localVue })
    const button = wrapper.find('button')
    await button.trigger('click')
    expect(actions.increment).toHaveBeenCalled()
  })
})
```

Once again, we call `createLocalVue` to create a local Vue instance we use for testing.

Then we have a `beforeEach` callback to set up the store and inject it into our app.

We inject the mocked store and the `localVue` object.

Then we get the button and trigger the `click` event on it.

And finally, we check that the `increment` action has been triggered after clicking the button.

### Mocking Getters

Similarly, we can mock getters in our tests.

Given that we have the following code:

`main.js`

```
import Vue from 'vue'
import App from './App.vue'
import Vuex from 'vuex'

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++
    }
  },
  actions: {
    increment(context) {
      context.commit('increment')
    }
  },
  getters: {
    count(state) {
      return state.count;
    }
  }
})

Vue.config.productionTip = false

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

`App.vue`

```
<template>
  <div id="app">
    <button @click="$store.dispatch('increment')">add</button>
    <p>{{count}}</p>
  </div>
</template>

<script>
import { mapGetters } from "vuex";

export default {
  name: "App",
  computed: {
    ...mapGetters(['count'])
  },
};
</script>
```

Then to test our code, we write:

```
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import App from '@/App'

const localVue = createLocalVue()

localVue.use(Vuex)

describe('App.vue', () => {
  let actions
  let store
  let getters

  beforeEach(() => {
    getters = {
      count: () => 2,
    }
    actions = {
      increment: jest.fn(),
    }
    store = new Vuex.Store({
      actions,
      getters
    })
  })

  it('dispatches "increment" action when button is clicked', () => {
    const wrapper = shallowMount(App, { store, localVue })
    expect(+wrapper.find('p').text()).toBe(getters.count())
  })
})
```

We add the mock getter into out mock `store` object.

Then we get the `p` element’s text and sees if it matches what’s returned in the mocked getter.

### Conclusion

We can mock Vuex and Vue Router in our tests so that our components can be tested in isolation.