storybook 可以獨立建立元件,並搭配 @storybook/addon-controls 控制 props 輸入及 @storybook/addon-actions 顯示 emit 輸出的內容,當然可以透過手動開啟 GUI 來測試元件是否符合規格,storybook 已經完成 render 的步驟,自動化的測試只差一步了!
storybook 可以搭配 interaction 套件進行元件測試,先按照「[Vue.js] storybook安裝方式與環境建置」完成 vue+storybook 的安裝,文內還是使用 Vue2,如果使用 Vue3 其實步驟差異不大。
安裝
執行指令
npm install --save-dev @storybook/addon-interactions @storybook/jest @storybook/testing-library
設定
請開啟 .storybook/main.js,註冊這個 addon interactions 套件,@storybook/addon-interactions 一定要放在 @storybook/addon-actions 或 @storybook/addon-essentials 的後面。
module.exports = {
addons: [
'@storybook/addon-actions', // or '@storybook/addon-essentials'
'@storybook/addon-interactions',
],
features: {
interactionsDebugger: true,
},
};
撰寫測試
寫測試前,要先了解測試元件的重點是「邏輯!」
第一視覺的邏輯,經由 props 或 slots 改變元件顯示上的差異,例如文字內容或是決定是否顯示哪些區域等;第二行為的邏輯,當使用者輸入或點擊按鈕等,此時畫面產生的變化或是 emit 的事件內容等。
使用 jest + testing library 做測試,testing library底層為 vue test utils,兩者相比 testing library 可以更聚焦於元件測試,不容易因為非邏輯的變更,連同測試都要一起做調整。
簡單寫一個元件 NameEditor.vue,有兩個 props,按下按鈕可以發出 event。
<template>
<div>
<div
v-text="message" />
<input
type="text"
placeholder="name"
v-model.trim="name">
<button
type="button"
@click="onButtonClick"
v-text="buttonText" />
</div>
</template>
<script>
import {ref} from 'vue';
export default {
props: {
message: {
type: String,
required: true,
},
buttonText: {
type: String,
required: true,
},
},
setup(props, {emit}) {
const name = ref(undefined);
const onButtonClick = () => {
emit('subit', name);
};
return {
name,
onButtonClick,
};
},
};
</script>
接著,NameEditor.stories.js story基本的內容:
import {userEvent, screen} from '@storybook/testing-library';
import {expect} from '@storybook/jest';
import NameEditor from './NameEditor';
export default {
title: 'NameEditor',
argTypes: {
message: {
control: 'text',
},
buttonText: {
control: 'text',
},
onNameSubmit: {
action: 'submit',
},
},
};
const Template = (args, {argTypes}) => ({
props: Object.keys(argTypes),
components: {
NameEditor,
},
template: `
<NameEditor
:message="message"
:button-text="buttonText"
@submit="onNameSubmit" />
`,
});
export const Default = Template.bind({});
Default.args = {
message: 'Input your name',
buttonText: 'Submit',
};
interaction 的測試,必須寫在 play 裡面,測試流程是先輸入文字後,按下按鈕:
Default.play = async ({args}) => {
const mockName = 'Chenuin';
const button = screen.getByRole('button');
await userEvent.type(screen.getByPlaceholderText('name'), mockName); // input name
await userEvent.click(button); // click button
const {message, buttonText, onNameSubmit} = args;
expect(onNameSubmit).toHaveBeenCalledTimes(1);
expect(onNameSubmit).toBeCalledWith(mockName);
expect(screen.getByText(message)).toBeTruthy();
expect(button.textContent).toBe(buttonText);
};
可以透過 args 取得 argTypes 的內容,發出的 event 自動 mock 可以直接驗證,只要把握 testing library獲取Dom的方式,需要注意 getBy... 和 queryBy... 兩者差別,前者若取不到任何相符條件的element,會拋出exception,後者不會。其他部分就是 jest 語法。
執行
開啟 storybook 就會自動執行測試內容,如果有錯誤也會將訊息顯示畫面。
npm run storybook
(畫面如下)
到這邊就告一段落,如果希望能夠導入CI,可以使用 @storybook/test-runner 。
npm install --save-dev @storybook/test-runner
開啟 package.json 加入 npm script
{
"scripts": {
"test-storybook": "npx test-storybook"
}
}
預設 test runner 會找到 http://localhost:6006 進行測試,可以加上 --url 來改變路徑。
npm run test-storybook
如果不是本地啟用storybook,可以利用 https://www.chromatic.com/ 來管理 storybook,就可以透過 internet 進行。Chromatic 可以用來發布 storybook、主要方便團隊 UI Review,藉此進行 CI 測試整合會更加容易。
參考資料
storybook 套件