はじめまして、フロントエンドエンジニアをしている川上です。
業務では、主にサービス要件に沿ったフロントエンド開発を担当しています。
今回は、私自身初めての経験だったフロントエンドのテスト導入手順や、
導入するにあたって気をつけた点などをお話しできればと思います。
はじめに
マイクロアドには様々なプロダクトがありますが、私の担当しているUNIVERSEというプロダクトについては、 フロントエンドフレームワークにVue.jsを利用しています。
テストの導入を行うにあたって、今後、現在利用しているVue2をVue3へ移行していくことを考えると*1
TypeScript*2でテストを書いていくのがいいということになりました。
また、TypeScriptのメリットでもある静的型付けは、大規模なコンポーネントのテストを行う際にテスト対象の引数や返り値の型の指定などができるので保守をしやすくなることもメリットです。
ライブラリには 、vue-test-utils
*3を利用します。
また、テストのフレームワークに関しては、Jestを利用していきます。
Vueコンポーネントのテストについて
Vueコンポーネントのテストには、
主に単体テストと結合テストがあります。
この記事では、単体テストを例に記述していこうと思います。
Vueコンポーネントの単体テストの詳細については、
以下の公式ページを参照してください。
Jestの導入について
Jestの導入についてですが、
ビルドとは切り分けてCI上でテストを実行する想定のため、
npm script
を利用します。
まず、必要なnpmモジュールをインストールします。
npm i --save-dev jest @types/jest babel-jest vue-jest ts-jest typescript @babel/core @babel/env
プロジェクトにBabelを利用しているため、
ビルド後のコードをJestでテストする必要性からbabel-jest
を導入しています。
Babelの設定を以下のように.babelrc
に記述します。
{ "presets": [ ["@babel/env", { "modules": false }] ], "env": { "test": { // test配下にテスト用の設定を記述 "presets": [ ["@babel/env", { "targets": { "node": "current" } }] ] } } }
次に、package.json
内に以下のように設定を記述していきます。
"jest": { "moduleFileExtensions": [ "js", "ts", "tsx", "vue" ], "globals": { "ts-jest": { "tsConfig": "tsconfig.json" } }, "transform": { "^.*\\.ts$": "ts-jest", "^.*\\.tsx$": "ts-jest", "^.*\\.vue$": "vue-jest" }, "moduleNameMapper": { "^@/(.*)$": "<rootDir>/src/$1" }, "testMatch": [ "<rootDir>/**/__tests__/*.(spec|test).ts?(x)" ] }
Vueコンポーネントのテストですが、
npm run jest
を実行した際に、
設定が読み込まれるように、package.json
内に記述しています。
transform
オプションを利用することによって、
テスト対象のファイルの拡張子によって、jest
/ts-jest
/vue-jest
を振り分けることが可能です。
特に指定していない場合は、通常のjest
が実行されます。
現時点で私が担当するプロダクトではTypeScriptを利用していませんが、
今後を見据えてts-jest
の設定を準備し、事前に準備しておいた
TypeScript用の設定ファイルである tsconfig.json
を適用します。
moduleNameMapper
オプションについては、
webpack.config.js
などで指定している場合もありますが、
今回はwebpack経由でのテストではないので再度設定してあげています。
Jestを利用したテスト
テストの記述についてですが、
説明すると長くなることもありここでは簡易なテストを例として書いていきます。
Vue
<template> <input type="text" :name="name" :value="value" @input="input($event)" > </template> <script> export default { props: { name: { type: String, required: true }, value: { type: Number, default: 0 } }, methods: { input(event) { if (event.target.value) { return this.$emit('emit-input', parseInt(event.target.value)) } else { event.target.value = null return this.$emit('emit-input', null) } } } } </script>
上記のVueファイルのテストを記述していきます。
方針としては、emit-input
がemitされる際に
inputに入力された値が正常に引数に設定されるかをテストします。
Jest + TypeScript
import { shallowMount, Wrapper } from '@vue/test-utils' import { Vue } from 'vue/types/vue' // vueファイルは現状tsで記述されていないためignore // @ts-ignore import JestTest from '@/src/jestTest' const testNullDataList = [null, ''] const testDataList = [ 0, 1, 10, 1000, 10000, 10000, 10102e3, 10000000000000000, 111111111111111111111 ] describe('jestTest.vue', () => { let inputWrapper: Wrapper<Vue> beforeEach(() => { // 子コンポーネントのマウントを行わないマウント const wrapper: Wrapper<Vue> = shallowMount(JestTest, { // propsプロパティの設定 propsData: { name: 'name' } }) inputWrapper = wrapper.find('input') }) test('input null test',() => { testNullDataList.forEach((testData, index) => { inputWrapper.setValue(testData) // emitted()['emit-input']にはemit('emit-input')が動作する際に // 引数に設定された値がスタックされている expect(inputWrapper.emitted()['emit-input'][index]).toEqual([null]) }) }) test('input number test', () => { testDataList.forEach((testData, index) => { inputWrapper.setValue(testData) expect(inputWrapper.emitted()['emit-input'][index]).toEqual([testData]) }) }) })
以上のテストファイルを適当なディレクトリに配置し、
npm run jest
を実行することによって、
Vueコンポーネントのテストを行うことが可能です。
おわりに
今回、私自身初めてのフロントエンドのテストを導入する
といった経験でしたが、なぜテストをする必要があるかや、
今後のことを見据えてなどの視点を持って導入できた点について
考えるいい機会になったと思います。
最後まで読んでいただきありがとうございました。
まだまだ学生を卒業して2年目のひよっこですが、
この記事がみなさんの開発の手助けになれば幸いです!