Vue 單元測試學習筆記
本篇是學習 vue unit test 的隨手筆記~
安裝
除了透過 VueCli 內建的自動配置安裝,也可參考[這個專案]的一些架構跟依賴進行手動安裝
這裡要特別注意相依套件的版本必須相同,如果直接安裝 jest 最新版本將有可能導致錯誤,因為 vue3-jest 可能還沒相應升級版本,可以參考這個討論
$ yarn add @vue/test-utils vue3-jest@27 jest@27 ts-jest@27 babel-jest@27 jest-environment-jsdom@27 -D
Babel
另外因為會需要用到 babel
,如果專案中原本沒有配置 babel 也要安裝下面這些套件
$ yarn add @babel/core @babel/preset-env -D
新增 babel.config.js
(這邊是範例,如果本來已經有配置可忽略這裡)
module.exports = {
presets: [
['@babel/preset-env',
{
targets: {
node: 'current'
}
}
]
]
}
配置
接下來要配置 package.json
以及 jest.config.js
如下
- package.json
{
"scripts": {
"test": "yarn jest"
}
}
- jest.config.js
module.exports = {
preset: 'ts-jest',
globals: {},
testEnvironment: 'jsdom',
transform: {
"^.+\\.vue$": "vue3-jest",
"^.+\\js$": "babel-jest"
},
moduleFileExtensions: ['vue', 'js', 'json', 'jsx', 'ts', 'tsx', 'node']
}
測試檔案路徑
預設會自動在專案資料夾下尋找 __test__
資料夾內的測試檔案進行處理
Jest 內建測試函數
describe
定義測試集
test/it
定義測試項目
beforeEach
定義每次測試項目之前執行的動作
describe('Test Task', () => {
let wrapper
const minLength = 6
// 在每一個 test() 執行前運行的一個函式,常會用來初始化 wrapper
beforeEach(() => {
wrapper = mount(Component)
})
it('Test Case', () => {
// ...
})
})
測試範例
初始化 Vue 組件
describe('TodoApp Test', () => {
const wrapper = mount(TodoApp, {
props: {
msg: 'Hello World'
},
data() {
return {
exist: false
}
}
})
})
取值, 查找元素, 檢查
// get: 查找,沒找到噴錯,通常用於必須存在時
// toBe: 檢查等於
expect(wrapper.get('[data-test="todo"]').text()).toBe('Profile');
// find: 查找,沒找到不噴錯,通常用於檢查是否存在時
// exists: 檢查存在
expect(wrapper.find('#admin').exists()).toBe(false);
// html: 獲取渲染的 html
// toContain: 檢查包含
expect(wrapper.html()).toContain('Hello World');
// isVisible: 檢查可視(display: none, opacity: 0, visibility: hidden)
expect(wrapper.get('#visible').isVisible()).toBe(false);
取得、修改狀態值
it('render admin link', async () => {
expect(wrapper.find('#admin').exists()).toBe(false);
// setData: 修改狀態
await wrapper.setData({ admin: true });
expect(wrapper.find('#admin').exists()).toBe(true);
})
it('check data value', async () => {
// vm: 取得 vue instance
expect(wrapper.vm.email).toBe(TEST_VALUE)
})
it('not render error if prop showError is false', async () => {
await wrapper.get('[data-test="password"]').setValue('abcde');
// setProps: 修改 props 資料
await wrapper.setProps({ showError: false });
expect(wrapper.find('[data-test="errorMsg"]').exists()).toBe(false);
})
取得、修改 DOM 值
it('check input value', async () => {
// setValue: 修改表單元素值
await emailInput.setValue(TEST_VALUE);
// element: 取得 DOM 元素
expect(emailInput.element.value).toBe(TEST_VALUE)
})
呼叫 setValue 的對象為 OPTION、CHECKBOX 或 RADIO 時, 如果沒有傳參數給 setValue 則表示為 checked
觸發 DOM Event
it('count became 1 after clicked once', async () => {
// trigger: 觸發 DOM 事件
await wrapper.get('[data-test="button"]').trigger('click');
expect(wrapper.get('[data-test="count"]').text()).toBe('1');
})
檢查、觸發 Emit Event
it('check emit triggered', async () => {
const count = wrapper.find('[data-test="count"]');
// 觸發事件
await count.trigger('click');
// 檢查觸發事件中包含 greet emit 事件
// emitted: 回傳一個紀錄元件發出的所有事件的物件,也可取得指定事件內容
// toHaveProperty: jest 檢查物件中是否存在某屬性
expect(wrapper.emitted()).toHaveProperty('greet');
const greetEvent = wrapper.emitted('greet');
// toEqual: 比較物件的所有屬性或陣列的所有元素是否相等
expect(greetEvent[0]).toEqual([1]);
})
// emitted 觸發兩次回傳結構
const emittedResponse = {
increment: [ [1], [2] ]
}
it('uses shallowMount', async () => {
const wrapper = shallowMount(App)
await wrapper.findComponent(Hello).vm.$emit('greet')
})