Vue.js
Vue.js 是一款易于使用、性能出色且用途广泛的框架,用于构建 Web 用户界面。您可以使用 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 组件
如果您使用的是 Web 框架Nuxt,WebdriverIO 将自动启用自动导入功能,并使测试您的 Vue 组件和 Nuxt 页面变得容易。但是,您可能在配置中定义的任何Nuxt 模块都需要 Nuxt 应用程序的上下文才能得到支持。
原因如下:
- WebdriverIO 无法仅在浏览器环境中初始化 Nuxt 应用程序。
- 使组件测试过度依赖于 Nuxt 环境会增加复杂性,我们建议将这些测试作为端到端测试运行。
WebdriverIO 还提供了一个服务,用于在 Nuxt 应用程序上运行端到端测试,有关信息,请参阅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({})