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
と少しのサスペンスラッパーを使用することをお勧めします。残念ながら、@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環境に過度に依存させると複雑になるため、これらのテストを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({})