【Vue Unit test】vue CLI 測 store (jest)

測試一個store,一般的想法都是先看 Vuex 文件:https://vuex.vuejs.org/guide/testing.html裡面內容是把mutations 、actions個別輸出,分別測試。

不過我的store規模不大,沒把 state、mutations 、actions模塊化,而是直接輸出一個 Vuex實例,所以沒有使用文件裡的測法。


一、測試想法

我的想法很簡單,就是測「actions 處理 response data 的邏輯是否正確」。方法是給 action mock response,然後查看 state 裡來自該 action 的 data 是否正確。

整個測試過程,直接跳過 mutations ,因為 mutations 的內容很單純,不涉及任何邏輯運算。


二、模擬假api response


在開始一切測項之前,先在測試文件裡引入要測的store

import store from '@/store/index'

然後模擬 api 的行為,讓它提供 action 假資料:

const data = {
   tokens: [
     { 
        product: 37,
        tokenId: "123",
        company: 53,
        datetime_added:"2021-05-28T03:56:24.448Z",
        id: 1
     },
     {
        product: 38,
        tokenId: "88883627838",
        company: 1,
        datetime_added: "2021-05-28T08:55:34.163Z",
        id: 2
      }
   ]
}

jest.mock('axios')
jset.get.mockResolvedValueOnce(data)

畢竟是模擬api,所以不是直接mock return data,而是mockResolvedValueOnce。

三、寫測試囉

跟測 component 不一樣的是,這邊沒有使用 mount, wrapper 一類的東西。因為在這邊就是測一個js file,不需要弄出一個 vue 實例。
it('Api: handel response', async() = {
  const answer = [
   {
     productId: 37,
     companyId: 53,
     addTime: "2021-05-28T03:56:24.448Z",
     transactionId: 1
   }, 
   { 
     productId: 38, 
     companyId: 1,
     addTime: "2021-05-28T08:55:34.163Z",
     transactionId: 2 }
  ] 

  await store.dispatch("getTransaction");

  const transaction = store.state.transaction;  
  expect(dongles).toEqual(answer); })
})

測試裡的第一個部分,就是先定義:根據假資料,最後data裡的東西應該是要長怎樣。

第二部分,就是執行 action,讓它call 假api。


最後一部分,撈出 state 裡的 data,判段它與第一部分定義的內容是否一致

大概就這樣完成它了。

四、mockRejectedValue

雖然大致完成了一個測試,但若要測試所有 actions,和所有 handle response 的邏輯,還需顧及返回error的部分:

axios.get.mockRejectedValueOnce(new Error(`mock err:${url}`));


至於再寫一個測試文件,專門讓 jest.mock('axios')模擬返回error的情況則更簡單。開一個新文件,把 jest.mock改寫成 return Promise.reject 就行了:


其他就是,寫測試常常需要用模擬的方式來取代依賴項目,很容易遇到「某個東西不存在」的情況(因為 jest.fn 不會自動模擬所有被模擬對象的細節)。這時就兵來將擋,水來土淹,缺什麼我就會在 jset.mock 裡塞什麼,務必讓測試能順利跑起來就是了。

例如這次專案,我有設置 axios.defaults.withCredentials = true。在測試的 jest.mock('axios')裡,我也放了一個 { withCredentials: true }。

還有很常見的 router.push,該怎麼 mock 就怎麼 mock。


五、小結

我認為這個測試的優點是原理簡單,至於是不是最佳解,我自己也不太確定。

因為只關注 actions 和 state,跳過了mutations,所以萬一測試失敗,它沒有辦法很直觀地反應是 action、mutations還是 state 的問題。不過我的 mutations 內容很簡單,只是收data、放data到state裡而已,不至於因此困擾。

最後,我總共測了5個 handle api 的 actions。假如store 的功能規模比較大的話,就不適合像這樣引入整個 Vuex.Store 實例了,屆時我應該會把 store 拆成 modules,然後分別用不同的文件測試。




留言