こんにちは!
今回は Vue3 でのコンポーネントのテストコードの作成方法を紹介します!
テストコードを書くことでチーム開発はもちろん、個人開発においてもコードをリファクタリングしたり、バグを発見したりする際に重要な役割を果たします。
Vue3 から新たに登場した Compostion API を利用することで、より保守性の高いコンポーネントを定義することができます。
コンポーネントをテストする場合、一般的にはテストコードを実装からできる限り切り離すように作成します。 理想的なのはコンポーネント内のロジックなどを考慮せずに実施するブラックボックス的なテストを書くことです。
本記事では Vue3 でのテストコードの作成方法について解説しております。
Vue2 では Vue.extend
でコンポーネントを定義する Options API で書くのが主流でしたが、 Vue3 では新たに Composition API と呼ばれるものがリリースされました。
Vue2 のプロジェクトで Composition API を使えるようにすることも可能ですが、 今回は Vue3 で動かすことを前提として解説します。
Vue3 に限らず、Web アプリケーションのフロントエンドのテストには、一般的に下記の3種類があります。
単体テストは、ビジネスロジックや汎用的な処理が意図した通りに動作するかをテストします。
単体テストを行うことで、新しい機能が構築されたり、コードがリファクタリングされてもアプリケーションが機能的で安定した状態を保つことができます。
Vue ではコンポーネント内の個々のロジックのテストをするイメージです。
Vue3 で使用される単体テストフレームワークは Jest
や Mocha
などがあります。
コンポーネントテストは、実装した Component が意図した通りのレンダリング・振る舞いを行なっているかをテストします。
Vue3 で使用されるコンポーネントテストライブラリは Vue Testing Library
や Vue Test Utils
などがあります。
Vue Testing Library
は Vue Test Utils
を抽象化したものなので、 Vue で初めてテストを書く場合は Vue Testing Library
を使用することをお勧めします。
E2E テストは、実際に利用するユーザーの操作シナリオを作成・自動実行し、Web ブラウザを通した一通りの操作が行えるかをテストします。
フロントエンドのコードだけではなく、バックエンドのコードやインフラ周りも含まれます。
こちらは実際の挙動に近いテストのため、今回の記事では解説は行いません。
※代表的なツールとしては Cypress などがあります。
今回は vue-cli
でプロジェクトを作成していきます。
$ vue create hello-world
Vue CLI v4.5.13
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, TS, Linter, Unit
? Choose a version of Vue.js that you want to start the project with 3.x
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? No
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Pick a unit testing solution: Jest
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
Manually select features
を選択して、TypeScript、テストフレームワークなどを選択します。
プロジェクトが作成されると、jest の config ファイルが自動で生成されます。
module.exports = {
preset: '@vue/cli-plugin-unit-jest/presets/typescript',
transform: {
'^.+\\.vue$': 'vue-jest'
}
}
また、下記のテストライブラリーがインストールされています。
ライブラリー名 | 現在の最新バージョン |
---|---|
@types/jest | ^24.0.19 |
@vue/cli-plugin-unit-jest | ~4.5.0 |
@vue/test-utils | ^2.0.0-0 |
vue-jest | ^5.0.0-0 |
Vue3 のアプリケーションの場合、複数のコンポーネントで構成され、
それぞれのコンポーネントが連動しながら動作します。
単体テストのコードはコンポーネントごとに書いていきます。
今回は Composition API を用いてコンポーネントの定義を行っていきます。
<template>
<div>
<div class="message">{{ uppercasedMessage }}</div>
<div class="count">
Count: {{ state.count }}
</div>
<button @click="increment">+</button>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, computed } from 'vue'
export default defineComponent({
props: {
message: {
type: String
}
},
setup(props) {
const state = reactive({
count: 0
})
const increment = () => {
state.count += 1
}
return {
state,
increment,
uppercasedMessage: computed(() => props.message?.toUpperCase())
}
},
})
</script>
上記は以下の機能を持ったサンプルコンポーネントです。
このコンポーネントを使ってテストコードを作成していきます。
先ほど作成したコンポーネントでテストする項目は 2点あります。
state.count
は 1ずつ増加するか実際に作成したコードがこちらです。
import { shallowMount } from '@vue/test-utils'
import Counter from '@/components/Counter.vue'
describe('Counter.vue', () => {
// propsでの文字列表示
it('renders a uppercase message', () => {
const message = 'hoge'
const wrapper = shallowMount(Counter, {
props: { message }
})
// 表示箇所の文字列のチェック
expect(wrapper.find(".message").text()).toBe('HOGE')
})
// ボタンクリックでのインクリメント
it('increments a count when button is clicked', async () => {
const message = 'hoge'
const wrapper = shallowMount(Counter, {
props: { message }
})
// クリックイベント発火
await wrapper.find('button').trigger('click')
// カウント表示箇所のチェック
expect(wrapper.find('.count').text()).toBe('Count: 1')
})
})
テストが通っている場合はこのような表示になると思います!
# テスト実行
$ yarn test:unit
PASS tests/unit/Counter.spec.ts
Counter.vue
✓ renders a uppercase message (76ms)
✓ increments a count when button is clicked (31ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.33s
Ran all test suites.
✨ Done in 7.61s.
失敗している場合は下記のように表示されます。 Expected
と Received
の出力の差分、エラー箇所がわかりやすく表示されるため、どこが原因となっているか素早く確認することができます。
FAIL tests/unit/Counter.spec.ts (6.304s)
Counter.vue
✕ renders a uppercase message (58ms)
✓ increments a count when button is clicked (13ms)
● Counter.vue › renders a uppercase message
expect(received).toBe(expected) // Object.is equality
Expected: "HOGE"
Received: "Count: 0"
10 | })
11 | // 表示箇所の文字列のチェック
> 12 | expect(wrapper.find(".count").text()).toBe('HOGE')
| ^
13 | })
14 |
15 | // ボタンクリックでのインクリメント
at Object.<anonymous> (tests/unit/Counter.spec.ts:12:43)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 7.511s
Ran all test suites.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Composition API により、state などの変更を行う関数を別ファイルで管理できるようになりました。
これにより、メソッドなどのロジックのみを独立させてテストすることが容易になります。
ロジックのみが記述されたファイルを下記のように作成します。
~/src/composition/counter.ts
import { reactive, computed } from '@vue/composition-api';
export default (defaultCount = 0) => {
const state = reactive({
defaultCount
})
const count = computed(() => state.defaultCount)
const increment = () => {
state.defaultCount += 1
}
const decrement = () => {
state.defaultCount -= 1
}
return {
count,
increment,
decrement
}
}
こちらは受け取った数字の上昇・減少を行う関数です。
先程のようにコンポーネント内でロジックを定義することもできますが、
このように分離させることでテスト時に簡潔に記述することができるのでおすすめです。
上記のロジックの場合、テストは下記のようになります。
~tests/unit/composition/useCount.spec.ts
import VueCompositionApi from '@vue/composition-api';
import { createLocalVue } from '@vue/test-utils';
import counter from '@/composition/counter';
const localVue = createLocalVue();
localVue.use(VueCompositionApi)
describe("counter", ()=> {
test("increment", ()=> {
const {count, increment} = counter(10)
expect(count.value).toEqual(10)
increment()
expect(count.value).toEqual(11)
})
test("decrement", ()=> {
const {count, decrement} = counter(10)
expect(count.value).toEqual(10)
decrement()
expect(count.value).toEqual(9)
})
})
テストコード内で localvue
を定義しておりますが、こちらはテズとコード内で Composition API を使うために必須の設定です。
コンポーネント内のロジックのみの分離によりロジックのテストは容易になりました。
view 側のコンポーネントテストなどについては Storybook を用いることをおすすめします。
今回は Vue3 で保守性の高いテストコードの作成方法について紹介しました。
Composition API でコンポーネントを定義しましたが、テストコードの作成においては Vue2 とあまり変わらないと思います。
Vue3 の Composition API を用いることでよりコンポーネントとロジックを疎結合な状態にでき、ロジックのみのテストも可能になりました。
Vue3 でのフロントエンドの開発のご相談は、ぜひお気軽にお問い合わせください。
スモールスタート開発支援、サーバーレス・NoSQLのことなら
ラーゲイトまでご相談ください
低コスト、サーバーレスの
モダナイズ開発をご検討なら
下請け対応可能
Sler企業様からの依頼も歓迎