I have been following these testing guidelines to test my vuex store. But when I touched upon the actions part, I felt there is a lot going on that I couldn't understand.
The first part goes like:
// actions.js
import shop from '../api/shop'
export const getAllProducts = ({ commit }) => {
commit('REQUEST_PRODUCTS')
shop.getProducts(products => {
commit('RECEIVE_PRODUCTS', products)
})
}
// actions.spec.js
// use require syntax for inline loaders.
// with inject-loader, this returns a module factory
// that allows us to inject mocked dependencies.
import { expect } from 'chai'
const actionsInjector = require('inject!./actions')
// create the module with our mocks
const actions = actionsInjector({
'../api/shop': {
getProducts (cb) {
setTimeout(() => {
cb([ /* mocked response */ ])
}, 100)
}
}
})
I infer that this is to mock the service inside the action.
The part which follows is:
// helper for testing action with expected mutations
const testAction = (action, payload, state, expectedMutations, done) => {
let count = 0
// mock commit
const commit = (type, payload) => {
const mutation = expectedMutations[count]
expect(mutation.type).to.equal(type)
if (payload) {
expect(mutation.payload).to.deep.equal(payload)
}
count++
if (count >= expectedMutations.length) {
done()
}
}
// call the action with mocked store and arguments
action({ commit, state }, payload)
// check if no mutations should have been dispatched
if (expectedMutations.length === 0) {
expect(count).to.equal(0)
done()
}
}
describe('actions', () => {
it('getAllProducts', done => {
testAction(actions.getAllProducts, null, {}, [
{ type: 'REQUEST_PRODUCTS' },
{ type: 'RECEIVE_PRODUCTS', payload: { /* mocked response */ } }
], done)
})
})
This is where it I find it difficult to follow.
My store looks like:
import * as NameSpace from '../NameSpace'
import { ParseService } from '../../Services/parse'
const state = {
[NameSpace.AUTH_STATE]: {
auth: {},
error: null
}
}
const getters = {
[NameSpace.AUTH_GETTER]: state => {
return state[NameSpace.AUTH_STATE]
}
}
const mutations = {
[NameSpace.AUTH_MUTATION]: (state, payload) => {
state[NameSpace.AUTH_STATE] = payload
}
}
const actions = {
[NameSpace.ASYNC_AUTH_ACTION]: ({ commit }, payload) => {
ParseService.login(payload.username, payload.password)
.then((user) => {
commit(NameSpace.AUTH_MUTATION, {auth: user, error: null})
})
.catch((error) => {
commit(NameSpace.AUTH_MUTATION, {auth: [], error: error})
})
}
}
export default {
state,
getters,
mutations,
actions
}
And This is how I am trying to test:
import * as NameSpace from 'src/store/NameSpace'
import AuthStore from 'src/store/modules/authorization'
const actionsInjector = require('inject!../../../../../src/store/modules/authorization')
// This file is present at: test/unit/specs/store/modules/authorization.spec.js
// src and test are siblings
describe('AuthStore Actions', () => {
const injectedAction = actionsInjector({
'../../Services/parse': {
login (username, password) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve({})
} else {
reject({})
}
}, 300)
})
}
}
})
it('Gets the user profile if the username and password matches', () => {
const testAction = (action, payload, state, mutations, done) => {
const commit = (payload) => {
if (payload) {
expect(mutations.payload).to.deep.equal(payload)
}
}
action({ commit, state }, payload)
.then(result => {
expect(state).to.deep.equal({auth: result, error: null})
})
.catch(error => {
expect(state).to.deep.equal({auth: [], error: error})
})
}
testAction(injectedAction.login, null, {}, [])
})
})
If I try to do this, I get:
"Gets the user profile if the username and password matches"
undefined is not a constructor (evaluating 'action({ commit: commit, state: state }, payload)')
"testAction@webpack:///test/unit/specs/store/modules/authorization.spec.js:96:13 <- index.js:26198:14
webpack:///test/unit/specs/store/modules/authorization.spec.js:104:15 <- index.js:26204:16"
I need help understanding what am I supposed to do to test such actions.
I know it's been awhile but I came across this question because I was having a similar problem. If you were to console.log injectedActions right before you make the testAction call you'd see that the injectedAction object actually looks like:
Object{default: Object{FUNC_NAME: function FUNC_NAME(_ref) { ... }}}
So the main solution here would be changing the testAction call to:
testAction(injectedAction.default.login, null, {}, [], done)
because you are exporting your action as defaults in your store.
A few other issues that are unrelated to your particular error... You do not need to manipulate the testAction boilerplate code. It will work as expected so long as you pass in the proper parameters. Also, be sure to pass done to testAction or your test will timeout. Hope this helps somebody else who comes across this!