MicroAd Developers Blog

マイクロアドのエンジニアブログです。インフラ、開発、分析について発信していきます。

Jestを使ったVueコンポーネントのテストを導入した話

はじめまして、フロントエンドエンジニアをしている川上です。
業務では、主にサービス要件に沿ったフロントエンド開発を担当しています。

今回は、私自身初めての経験だったフロントエンドのテスト導入手順や、 導入するにあたって気をつけた点などをお話しできればと思います。
 

はじめに

マイクロアドには様々なプロダクトがありますが、私の担当しているUNIVERSEというプロダクトについては、 フロントエンドフレームワークにVue.jsを利用しています。

jp.vuejs.org

テストの導入を行うにあたって、今後、現在利用しているVue2をVue3へ移行していくことを考えると*1 TypeScript*2でテストを書いていくのがいいということになりました。 また、TypeScriptのメリットでもある静的型付けは、大規模なコンポーネントのテストを行う際にテスト対象の引数や返り値の型の指定などができるので保守をしやすくなることもメリットです。
 
ライブラリには 、vue-test-utils*3を利用します。
また、テストのフレームワークに関しては、Jestを利用していきます。

jestjs.io

Vueコンポーネントのテストについて

Vueコンポーネントのテストには、 主に単体テストと結合テストがあります。 この記事では、単体テストを例に記述していこうと思います。  
Vueコンポーネントの単体テストの詳細については、
以下の公式ページを参照してください。

jp.vuejs.org

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年目のひよっこですが、 この記事がみなさんの開発の手助けになれば幸いです!

*1:Vue3はVue2よりTypeScriptを扱いやすいため

*2:JavaScriptの正式規格であるES6+を基に作られた静的型付け言語

*3:Vueコンポーネントテスト用の公式ライブラリ