vuex
怎麼單元測試,咱們只能經過檢驗state的值是否符合預期來測試,因此,正常的套路應該是測試mutation,而後看看對應的state是否發生了符合預期的變化。沒錯。原文地址vue單元測試vuex,mutation,尤爲是actions、getters怎麼測?讓你像使用vuex同樣測試vuexjavascript
好比這種html
SET_LIST(state, payload) {
state.listData = payload;
},
複製代碼
// test.spec.js
describe("mutations", async () => {
it("mutations SET_LIST", async function() {
SET_LIST(state, [123]);
expect(state.listData).eql([123]);
});
}
複製代碼
每一個mutation就是一個方法,咱們直接調用 SET_LIST(state, payload)
檢驗對應的state就能夠完成對SET_LIST
的測試了。這固然簡單了,畢竟mutation裏面就這兩個參數vue
action裏面通常伴有發送請求,而後根據請求結果,觸發對應的mutation,有時咱們還會在action裏面觸發另外的action等較複雜的狀況 下面列舉了咱們不肯意測action的緣由:java
在測試這樣的action時是否是會吐槽,action怎麼會寫這麼複雜。哈哈,業務須要啊。 action裏面也是兩個參數,只是第一個參數context
是一個對象 ios
state
、
rootState
、
getters
、
rootGetters
這幾個對象用來獲取值之外,裏面還有
commit
、
dispatch
方法。 好比下面的兩個action就比較複雜,還有「嵌套」關係
async _getDetail({ rootState, dispatch }, params) {
const [res] = await Promise.all([
rootState.Axios.post(rootState.Api.pim.propGetAttr, params),
rootState.pim.root.langResult
]);
dispatch("handleDetailResponse", { res, params });
return res;
},
// 處理新建/編輯屬性的通用response
async handleDetailResponse({ commit, dispatch }, { res, params }) {
if (httpSuccess(res)) {
commit("SET_DETAIL", res.data.data.data);
const paginator = (res.data.data && res.data.data.paginator) || {};
if (paginator.totalCount > paginator.limit) {
dispatch("_getDetailAllValueVOList", {
...params,
page: {
page: 1,
size: paginator.totalCount
}
});
} else {
const { valueVOList } = res.data.data.data || {};
commit("SET_ATTR_VALUES", valueVOList);
commit("SET_ATTR_VALUE_PAGINATOR", paginator);
}
}
},
複製代碼
_getDetail
這個action呢?跟mutation同樣?不行啊,第一個參數{commit, dispatch}
的commit
和dispatch
怎麼傳?this
怎麼辦?this.dispatch
或者this.state.pim.listData
這種寫法怎麼辦?action裏面的this
是含有下列屬性的。 git
測試action的童鞋天然是發現這個問題的,因此,你們通常都選擇了「曲線救國」--拆分action測試github
上圖所示的Action1的測試會分紅2個部分進行測試。面試
其實Action1 -> Action2
好很差測試,我這裏存疑。 好多人都是用sinon
來實現測試action,sinon
怎麼用我不知道,我只是經過代碼來看,這種測試action的沒什麼用,就是爲了提交單元測試覆蓋率而已。vuex
直接複用store裏面的全部代碼來實現全流程測試(接口數據確定得模擬了) 這樣的話,擋在前面的問題就出現了,在store的方法裏面的commit
,dispatch
,this
怎麼辦? 咱們不是學過使用bind
,call
嗎?難道這些只是爲了面試的嗎,遇到問題解決問題啊。 store的每一個module就是一個對象啊,裏面有屬性state
、mutations
、actions
、getters
而已。按照vuex的使用方式模擬一個基本跟原有commit
,dispatch
同樣功能的函數便可。 先看效果。 原有store以下,有多層子module:npm
// store.js
export default {
namespace: true,
state: {
abc: 1
},
mutations: {
SET(state, payload) {
state.abc = payload;
},
PLUS(state, payload) {
state.abc = state.abc + payload;
},
},
actions: {
_plus(context, params) {
console.log("context -> ", context);
context.commit("PLUS", params);
}
},
getters: {
getStatePlus(state, getters, rootState, rootGetters) {
console.log(
"getters getStatePlus-> ",
state,
rootState,
Object.getOwnPropertyNames(getters),
Object.getOwnPropertyNames(rootGetters)
);
return state.abc + 1;
}
},
modules: {
sub: {
namespace: true,
state: {
cbd: 100
},
mutations: {
SET(state, payload) {
state.cbd = payload;
},
MINUS(state, payload) {
console.log(" MINUS arg-> ", arguments);
state.cbd = state.cbd - payload;
console.log(" MINUS result-> ", state.cbd);
}
},
actions: {
_minus(context, params) {
console.log("context -> ", context);
context.commit("MINUS", params);
}
},
getters: {
getStateMinus(state, getters, rootState, rootGetters) {
console.log(
"getters getStateMinus-> ",
state,
rootState,
Object.getOwnPropertyNames(getters),
Object.getOwnPropertyNames(rootGetters)
);
return state.cbd - 1;
}
},
modules: {
subTie: {
namespace: true,
state: {
xyz: 55
},
mutations: {
SET(state, payload) {
state.xyz = payload;
},
MULTIPLY(state, payload) {
state.xyz = state.xyz * payload;
}
},
actions: {
_multiply(context, params) {
console.log("context -> ", context);
context.commit("MULTIPLY", params);
}
},
getters: {
getStateMultiplyDouble(state) {
console.log("getters getStateMultiplyDouble-> ", arguments);
return state.xyz * 2;
}
}
}
}
}
}
};
複製代碼
在測試用例裏面使用也很簡單,跟咱們在action裏面觸發mutation和action一致,是否是很爽。
// test.spec.js
import { expect } from "chai";
import VuexTester from 'vuex-tester';
import store from '../store';
const {commit, dispatch, rootState, state, getters, rootGetters} = new VuexTester(store).update();
describe("test state", async () => {
it("state abc", async function() {
expect(state.abc).eql(1);
});
});
describe("test rootState", async () => {
it("rootState abc", async function() {
expect(rootState.abc).eql(1);
});
});
describe("test mutations", async () => {
it("mutations SET", async function() {
commit('SET', 100);
expect(state.abc).eql(100);
});
it("mutations PLUS", async function() {
commit('SET', 100);
commit('PLUS', 10);
expect(state.abc).eql(110);
});
});
describe("test actions", async () => {
it("actions _plus", async function() {
commit('SET', 100);
dispatch('_plus', 9);
expect(state.abc).eql(109);
});
});
describe("test getters", async () => {
it("getters getStatePlus", async function() {
commit('SET', 100);
expect(getters.getStatePlus).eql(101);
});
});
describe("test rootGetters", async () => {
it("rootGetters getStatePlus", async function() {
commit('SET', 100);
expect(rootGetters.getStatePlus).eql(101);
});
});
複製代碼
我放部分核心代碼
update(storeContext = this.store, namespace = this.namespace) {
const fn = this.getFn(namespace);
const {
state = {},
actions = {},
mutations = {},
getters = {}
} = storeContext;
const boundCommit = (type, payload) => {
console.log("[commit ]: ", type, payload);
// mutation in vuex return noting, but we return state
return (
fn(type, this.rootMutationsMap, mutations).call(
this.storeContext,
state,
payload
) || state
);
};
const boundDispatch = (type, payload) => {
console.log("[dispatch]: ", type, payload);
return fn(type, this.rootActionsMap, actions).call(
this.storeContext,
this.context,
payload
);
};
this.storeContext.commit = boundCommit;
this.storeContext.dispatch = boundDispatch;
this.initGetter(namespace, getters, state);
// core state and function in vuex context
this.context = {
rootState: this.storeContext.state,
state: state,
commit: boundCommit,
dispatch: boundDispatch,
getters: this.gettersMap,
rootGetters: this.rootGettersMap
};
return this.context;
}
複製代碼
哈哈,看到你心心念唸的commit
,dispatch
,state
,getters
,this
了吧 我把本身的想法發到了npm上面,地址vuex-tester。 具體代碼在github上面。 建議clone下來,直接運行npm run test
進行測試。