在寫元件時最常見的就是會使用 v-if 來動態插入和刪除元素,我們馬上來看看下面的範例程式。
const Component = {
template: `
<nav>
<a id="profile" href="/profile">My Profile</a>
<a v-if="admin" id="admin" href="/admin">Admin</a>
</nav>
`,
data () {
return {
admin: false
}
}
}
在元件中有兩個連結,一個為指向 /profile 的連結,另一個為指向 /admin 的連結,不過 /admin 連結只有在 admin 值為 true 時才會顯示。
要測試這個元件是否正常,我們應該驗證三種情況是否正確運行:
test('always render profile link', () => {
const wrapper = mount(Component)
const profileLink = wrapper.get('#profile')
expect(profileLink.text()).toBe('My Profile')
})
語法說明:
get() : VueWrapper 有一個 get 方法來搜索現有元素,它使用和 Document.querySelector() 一樣的語法,如果 get() 沒有找到目標元素,它會拋出錯誤並導致測試失敗。如果找到的話則會回傳一個 DOMWrapper。
DOMWrapper 是圍繞 Wrapper API 的 DOM 元素的瘦包裝器 (thin wrapper)。
text() : 回傳元素的文本內容 (text content)。
test('does not render an admin link', () => {
const wrapper = mount(Component)
expect(wrapper.find('#admin').exists()).toBe(false)
})
語法說明:
find(): find() 和 get() 很像,一樣是使用 Document.querySelector() 的語法,不過差別在於 find() 沒有找到目標元素時不會拋出錯誤。
除非斷言的內容可能不存在,否則盡量都使用 get 而不是 find ,因為如果不存在時就表示真的有錯誤。
exist(): 檢查元素是否存在。
test('only render admin link when admin is true', () => {
const wrapper = mount(Component, {
data () {
return {
admin: true
}
}
})
expect(wrapper.get('#admin').text()).toBe('Admin')
})
語法說明:
有時候我們只想隱藏一個元素,但不想將它從 DOM 中移除,此時我們可以使用 v-show 來實現這件事。
const Component = {
template: `
<nav>
<a id="user" href="/profile">My Profile</a>
<ul v-show="expandDropdown" id="user-dropdown">
<!-- dropdown content -->
</ul>
</nav>
`,
data () {
return {
expandDropdown: false
}
}
}
不過和上面的例子不同的是,元素雖然不會渲染出來,但它其實是存在於 DOM tree 中的,所以如果使用 get() 或 find() 都會確實找到目標元素,但這其實不是我們想要的結果。
因此面對這樣的情況,我們可以使用 isVisible() 來解決這個問題,isVisible 是專門用來檢查元素是否為隱藏的狀態,例如:
test('does not show the user dropdown', () => {
const wrapper = mount(Component)
expect(wrapper.get('#user-dropdown').isVisible()).toBe(false)
})
在寫測試時,我們總是會透過 get() 或 find() 來尋找目標的元素,而通常我們要尋找元素時使用的選擇器 (selectors) 可能習慣會是透過 id 或者是 class,然而我想建議大家使用 dataset 來當作選擇器,如下:
import { mount } from '@vue/test-utils'
const Component = {
template: '<div data-test="target">dataset</div>'
}
test('render dataset', () => {
const wrapper = mount(Component)
expect(wrapper.get('[data-test="target"]').text()).toBe('dataset')
})
原因是 id 或 class 通常都是因為其他用途而存在的,所以我們可能會在修改程式碼的過程中而不小心去修改 id 或是 class 造成測試不通過,那如果是用 dataset 的話, 就不會有這個問題了,甚至在開發時也很清楚知道這段程式碼和測試有關而特別小心修改。
今天的分享就到這邊,如果大家對我分享的內容有興趣歡迎點擊追蹤 & 訂閱系列文章,如果對內容有任何疑問,或是文章內容有錯誤,都非常歡迎留言討論或指教的!
明天要來分享的是 Vue3 單元測試 (Unit Testing) 主題的第四篇 Event Handling ,那我們明天見!
在善用 dataset 這裡的意思是,我們要刻意提供一個 data-test
給 component 嗎?
TD 沒錯,我們要刻意提供一個 data-test 給 HTML tag,原因在內容也有提及,因為通常 id 和 class 都是因為其他原因而添加上去的,不會想到和測試有關,也因此在日後維護時容易更動到導致測試不通過。