Vue.js
Vue.js 是一個平易近人、高效能且多功能的框架,用於建構網頁使用者介面。 您可以使用 WebdriverIO 及其瀏覽器執行器,直接在真實瀏覽器中測試 Vue.js 元件。
設定
若要在您的 Vue.js 專案中設定 WebdriverIO,請依照我們元件測試文件中的指示。 請務必在執行器選項中選取 `vue` 作為預設值,例如:
// wdio.conf.js
export const config = {
// ...
runner: ['browser', {
preset: 'vue'
}],
// ...
}
Vue 預設值需要安裝 ` @vitejs/plugin-vue`。 我們也建議使用 Testing Library 將元件渲染到測試頁面中。 因此,您需要安裝下列額外依賴項
- npm
- Yarn
- pnpm
npm install --save-dev @testing-library/vue @vitejs/plugin-vue
yarn add --dev @testing-library/vue @vitejs/plugin-vue
pnpm add --save-dev @testing-library/vue @vitejs/plugin-vue
然後您可以執行以下命令來開始測試
npx wdio run ./wdio.conf.js
編寫測試
假設您有以下 Vue.js 元件
<template>
<div>
<p>Times clicked: {{ count }}</p>
<button @click="increment">increment</button>
</div>
</template>
<script>
export default {
data: () => ({
count: 0,
}),
methods: {
increment() {
this.count++
},
},
}
</script>
在您的測試中,將元件渲染到 DOM 中並對其執行斷言。 我們建議使用 `@vue/test-utils` 或 `@testing-library/vue` 來將元件附加到測試頁面。 若要與元件互動,請使用 WebdriverIO 命令,因為它們的行為更接近實際使用者互動,例如:
- @vue/test-utils
- @testing-library/vue
import { $, expect } from '@wdio/globals'
import { mount } from '@vue/test-utils'
import Component from './components/Component.vue'
describe('Vue Component Testing', () => {
it('increments value on click', async () => {
// The render method returns a collection of utilities to query your component.
const wrapper = mount(Component, { attachTo: document.body })
expect(wrapper.text()).toContain('Times clicked: 0')
const button = await $('aria/increment')
// Dispatch a native click event to our button element.
await button.click()
await button.click()
expect(wrapper.text()).toContain('Times clicked: 2')
await expect($('p=Times clicked: 2')).toExist() // same assertion with WebdriverIO
})
})
import { $, expect } from '@wdio/globals'
import { render } from '@testing-library/vue'
import Component from './components/Component.vue'
describe('Vue Component Testing', () => {
it('increments value on click', async () => {
// The render method returns a collection of utilities to query your component.
const { getByText } = render(Component)
// getByText returns the first matching node for the provided text, and
// throws an error if no elements match or if more than one match is found.
getByText('Times clicked: 0')
const button = await $(getByText('increment'))
// Dispatch a native click event to our button element.
await button.click()
await button.click()
getByText('Times clicked: 2') // assert with Testing Library
await expect($('p=Times clicked: 2')).toExist() // assert with WebdriverIO
})
})
您可以在我們的範例儲存庫中找到適用於 Vue.js 的 WebdriverIO 元件測試套件的完整範例。
在 Vue3 中測試非同步元件
如果您使用的是 Vue v3,並且正在測試像以下的非同步元件
<script setup>
const res = await fetch(...)
const posts = await res.json()
</script>
<template>
{{ posts }}
</template>
我們建議使用 `@vue/test-utils` 和一個小的 suspense 包裝器來取得渲染的元件。 遺憾的是,`@testing-library/vue` 目前尚不支援此功能。 建立一個包含以下內容的 `helper.ts` 檔案
import { mount, type VueWrapper as VueWrapperImport } from '@vue/test-utils'
import { Suspense } from 'vue'
export type VueWrapper = VueWrapperImport<any>
const scheduler = typeof setImmediate === 'function' ? setImmediate : setTimeout
export function flushPromises(): Promise<void> {
return new Promise((resolve) => {
scheduler(resolve, 0)
})
}
export function wrapInSuspense(
component: ReturnType<typeof defineComponent>,
{ props }: { props: object },
): ReturnType<typeof defineComponent> {
return defineComponent({
render() {
return h(
'div',
{ id: 'root' },
h(Suspense, null, {
default() {
return h(component, props)
},
fallback: h('div', 'fallback'),
}),
)
},
})
}
export function renderAsyncComponent(vueComponent: ReturnType<typeof defineComponent>, props: object): VueWrapper{
const component = wrapInSuspense(vueComponent, { props })
return mount(component, { attachTo: document.body })
}
然後導入並測試元件,如下所示
import { $, expect } from '@wdio/globals'
import { renderAsyncComponent, flushPromises, type VueWrapper } from './helpers.js'
import AsyncComponent from '/components/SomeAsyncComponent.vue'
describe('Testing Async Components', () => {
let wrapper: VueWrapper
it('should display component correctly', async () => {
const props = {}
wrapper = renderAsyncComponent(AsyncComponent, { props })
await flushPromises()
await expect($('...')).toBePresent()
})
afterEach(() => {
wrapper.unmount()
})
})
在 Nuxt 中測試 Vue 元件
如果您使用的是網頁框架 Nuxt,WebdriverIO 將會自動啟用自動導入功能,並讓測試您的 Vue 元件和 Nuxt 頁面變得容易。 然而,您可能在設定中定義的任何 Nuxt 模組,並且需要 Nuxt 應用程式的環境,則無法支援。
原因如下
- WebdriverIO 無法僅在瀏覽器環境中啟動 Nuxt 應用程式
- 讓元件測試過度依賴 Nuxt 環境會造成複雜性,我們建議將這些測試作為 e2e 測試執行
WebdriverIO 也提供一項服務,用於在 Nuxt 應用程式上執行 e2e 測試,如需相關資訊,請參閱`webdriverio-community/wdio-nuxt-service`。
模擬內建的可組合項
如果您的元件使用原生 Nuxt 可組合項,例如 `useNuxtData`,WebdriverIO 將會自動模擬這些函式,並允許您修改其行為或對其執行斷言,例如:
import { mocked } from '@wdio/browser-runner'
// e.g. your component uses calls `useNuxtData` the following way
// `const { data: posts } = useNuxtData('posts')`
// in your test you can assert against it
expect(useNuxtData).toBeCalledWith('posts')
// and change their behavior
mocked(useNuxtData).mockReturnValue({
data: [...]
})
處理第三方可組合項
所有可以增強您 Nuxt 專案的第三方模組都無法自動模擬。 在這些情況下,您需要手動模擬它們,例如,假設您的應用程式使用 Supabase 模組外掛程式
export default defineNuxtConfig({
modules: [
"@nuxtjs/supabase",
// ...
],
// ...
});
並且您在可組合項中的某處建立 Supabase 的執行個體,例如:
const superbase = useSupabaseClient()
測試將會因為下列原因而失敗
ReferenceError: useSupabaseClient is not defined
在此,我們建議模擬使用 `useSupabaseClient` 函式的整個模組,或建立一個模擬此函式的全域變數,例如:
import { fn } from '@wdio/browser-runner'
globalThis.useSupabaseClient = fn().mockReturnValue({})