Reputation: 2356
I've been trying to get my head around unit testing Vue components, but I can't seem to quite work out how to Mock/Stub out store objects and methods that call an API async.
This is an example of a Vue component we have:
import { mapState, mapGetters } from 'vuex'
import router from 'components/admin/router'
export default {
name: 'Users',
computed: {
...mapState('admin', [
'users',
]),
...mapGetters({
requestInProgress: 'requestInProgress',
}),
},
data: function() {
return {
filterTerm: '',
usersLoaded: false,
}
},
methods: {
getUsers(filter) {
this.$store.dispatch('admin/getUserList', filter)
.then(res => {
this.usersLoaded = true
})
.catch(e => {
this.$toast.error({
title: 'Failed to retrieve data',
message: this.$options.filters.normaliseError(e),
})
})
},
},
mounted() {
this.getUsers('*')
},
}
And this is the test I want to write. I can't even get the test to run cleanly without actually trying to assert anything
import Vue from 'vue'
import { shallowMount } from '@vue/test-utils'
import Users from 'components/admin/pages/user/users.vue'
describe('Users Vue', () => {
it('Page Should Load', () => {
const mockResponse = {
data: [{
"id": "1",
"emailAddress": "[email protected]",
"firstName": "Luke",
"lastName": "Niland",
"staffNumber": "12345",
"phoneNumber": "07707 999999",
"active": true
}
]};
let actions
let store
beforeEach(() => {
actions = {
'admin/getUserList': sinon.stub()
.returns(Promise.resolve(mockResponse))
}
store = new Vuex.Store({
state: {},
actions
})
})
const wrapper = shallowMount(Users, { store })
const h5 = wrapper.find('h5')
expect(h5.text()).toBe('User Administration')
})
})
The errors I tend to get back are about items being undefined, normally, in this case, $store.dispatch
is undefined
. I feel like I'm missing something with the mocking somewhere, or the fact that the getUsers()
being called on the mount is tripping it up.
Upvotes: 5
Views: 5110
Reputation: 3225
In order to test a Vue component mocking Vuex as you're doing in your example, it is ok to pass a mock store
to Vue when you shallowMount
your component as you're doing, so:
shallowMount(Users, { store })
but this mock store
needs to be mounted also to the base Vue constructor. To do so, you have to pass it to a - localVue
. A localVue
is a scoped Vue constructor that you can make changes to, in the testing scope, without affecting the global Vue constructor actually used in your application.
Moreover, in your specific case, you did not neither imported nor installed Vuex.
Then, to properly configure your test you need to:
localVue
instance by invoking the Vue Test Utils utility createLocalVue
function and install Vuex on it: import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
//creating the local Vue instance for testing
const localVue = createLocalVue()
//mounting Vuex to it
localVue.use(Vuex)
shallowMount
function adding also the localVue
instance to the payload: const wrapper = shallowMount(Users, { store, localVue })
See here for the Official Documentation references.
Another useful resource about Vue testing is this book (for your specific case, see Chapter 7) and its GitHub repository
Upvotes: 2
Reputation: 223
You must create a local Vue to your tests and install Vuex plugin:
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
const localVue = createLocalVue()
localVue.use(Vuex)
const wrapper = shallowMount(...)
Upvotes: 1