Vue.js Vueコンポーネントのユニットテストを書いてみよう

※このエントリは[Vue.jsアドベントカレンダー](http://qiita.com/advent-calendar/2016/vue)10日目の記事です。

最近の開発はjQueryでプロトタイピングだけ行って、本開発はVue.jsをベースに行っているPotato4dです。

Vue.jsは非常に強力で便利なフレームワークですが、そのテストについての情報というのは、探しても中々見つからないため、困っているかたも多いのではないでしょうか。

今回は、そんな問題を解消するために、Vue.jsのコンポーネントをテストしていく手法について共有していきたいと思います。

## 開発の準備

### 今回の環境

今回は、簡単に導入して試していくために、Vue.js公式のコマンドラインツール`vue-cli`を用いて開発を行っていきます。

vue-cliは、Vue.jsを用いた様々なプロジェクトの雛形を自動生成してくれるツールとなり、Node製であるため、Vueの開発環境が整っていれば、すぐに導入し、開発を始めることが可能です。

以下の手順に従って、vue-cliを導入、雛形を作成しておいてください。

```bash:terminal
$ npm i -g vue-cli
$ vue init webpack vue-test

? Project name                         vue-test
? Project description                  A Vue.js project
? Author                               Your Name
? Vue build                            standalone
? Use ESLint to lint your code?        No
? Setup unit tests with Karma + Mocha? Yes
? Setup e2e tests with Nightwatch?     No

$ cd $_
$ npm i
```

### テスト環境の疎通確認

環境が構築できたら、はじめにデフォルトで作成されているテストがしっかり動くかを確認しておいてください。プロジェクトルート上で`npm run test`を行い、テストが通ると成功です。

## 仕様の決定

それでは、
環境の構築ができましたので、次に、実際にテストを書くための仕様を決めていきます。
今回は例として、以下のようなVueコンポーネントを実装することとしました。

* ファイル名を`Greeting.vue`とし、これをテストの対象とする。
* `greeting`は、同名のタグをもつVueコンポーネントを実装する。
* `greeting`はpropsにて`person`をうけとり、その内容を"Hello, {name}."の形式で`<p>`タグ内に表示する。
* `Greeting.vue`は`src/components`に配置されていることを前提とする。

## テストコードの記述

それでは、いよいよ実際にテストコードを書いていきたいと思います。
今回は自動生成されている`test/unit/specs/`に、`Greeting.spec.js`を作成し、テストコードを書いていきたいと思います。

また、今はまだ中身を記述しませんが、ファイルが無いとテスト時にエラーが発生するため、`src/components/Greeting.vue`も作成しておきます。

最低限の動作保証のため、Gretting.vueの中身を以下のように編集しておきます。

```html:Greeting.vue
<template lang="html">
  <div>
  </div>
</template>
```

その上で、テストコードを書いていきます。
今回の要件を満たすもっとも簡潔なソースコードは、以下のようなものとなります。

```js:Greeting.spec.js
import Vue from 'vue'
import Greeting from 'src/components/Greeting.vue'

function getInstance (Component, propsData) {
  const Ctor = Vue.extend(Component)
  const vm = new Ctor({ propsData }).$mount()
  return vm
}

describe('Greeting.vue', () => {
  it('<p> tag content equal props data `person`', () => {
    const instance = getInstance(Greeting, {person: 'john'});

    expect(instance.$el.querySelector("p").textContent)
    .to.equal('Hello, john.')
  })
})
```

なお、このコードはVue.js公式ドキュメント「単体テスト」の項のソースコードを基にしています。  
[https://jp.vuejs.org/v2/guide/unit-testing.html#テストしやすいコンポーネントの記述](https://jp.vuejs.org/v2/guide/unit-testing.html#テストしやすいコンポーネントの記述)


### 解説

基本的なテストコードの記述の流れとしては、`describe()`内を一つの単位とし、(今回の場合はコンポーネント一つが一つの単位となります。これは、他には例えばクラスファイルなどが単位の一つとしてあげられます)`it()`内でどういった機能についてのテストかを明示的に宣言し、`expect()`内で実際に動作を検証する流れとなります。

### describe / it / expectについて

これらはテストフレームワークの構文で、それぞれが以下のような役割を持ちます。

#### describe

describeは、何に対してのテストなのかを示して、その中でテストを記述していく関数です。
今回の場合、Greeting.vueに対してのテストであることを示しています。

これは、mochaというテストフレームワークの機能です。

#### it

itは、describe内のいち機能についてのテストであることを示す関数です。
今回の場合、Greeting.vueに対してで、かつpersonのpropが正しく動作することのテストであることを示しています。

これは、mochaというテストフレームワークの機能です。

#### expect

expectは、実際のアサーション(テストがあってるかどうかのチェック本体)を行うための関数です。
今回の場合、propsとして送信したデータと、実際のインスタンスの`<p>`タグの中身が一致しているかどうかをテストしています。

これは、mocha本体が持っている機能ではなく、今回の場合はchaiというツールの機能を利用しています。

#### getInstanceについて

まず前提ですが、Vue.jsのテストは、テストランナー`Karma`とヘッドレスブラウザを利用することで、ブラウザの動作をエミュレートすることで行っています。

そのため、テストの前には、ルートコンポーネントでは擬似的にDOM要素を生成してnewを、それ以外でも、コンポーネントを登録してしっかりと初期化してやる必要があります。

基本的には、`Vue,extend()`を用いてコンポーネントとして登録し、そのインスタンスに対して、通常のVueのコードと同じように記述していくこととなります。




## テストの実行

テストコードを書き終えたので、まずは実行してみます。
正常にテストが記述できていれば、まだ何も作られていないため、エラーが発生するはずです。

```text:terminal
$ npm run test

TOTAL: 1 FAILED, 1 SUCCESS

1) <p> tag content equal props data `person`
     Greeting.vue
     expected '' to equal 'Hello, john.'

=============================== Coverage summary ===============================
Statements   : 100% ( 11/11 )
Branches     : 100% ( 4/4 ), 1 ignored
Functions    : 100% ( 2/2 )
Lines        : 100% ( 5/5 )
================================================================================

$
```

予想通り、エラーが発生していますね。
これでテストコードは問題ないので、次はVueコンポーネントを実装していきたいと思います。

## テスト結果に基づいた実装

それでは、実際に仕様どおりかつテストをに通るコードを書いてみます。自由に書いてくださって構いませんが、今回は一例として、以下のような実装を掲載しておきます。

```diff:greeting.vue
<template>
  <div>
+++    <p>Hello, {{ person }}.</p>
  </div>
</template>

+++ <script>
+++ export default {
+++   props: ["person"]
+++ }
+++ </script>
```

## 動作確認

実装が完了したので、もう一度テストを実行し、問題ないか確認します。

```text:terminal
$ npm run test

.
.
.
  Greeting.vue
    ✓ <p> tag content equal props data `person`

TOTAL: 2 SUCCESS

=============================== Coverage summary ===============================
Statements   : 100% ( 11/11 )
Branches     : 100% ( 4/4 ), 1 ignored
Functions    : 100% ( 2/2 )
Lines        : 100% ( 5/5 )
================================================================================

$
```

これで問題なさそうですね。無事実装が完了しました。

## まとめ

この記事は、[Vue.jsの日本語Slack](https://vuejs-jp-slackin.herokuapp.com/)にて、丁度コンポーネントのテストについての話題があがっていることをきっかけとして書くこととしました。

私自身、話題にあがっている時に丁度Vue.jsのテストについて意識し始めたところだったのですが、どうもViewと密接に関わってくるJavaScriptフレームワークのテストには苦手意識を持っていました。

しかし、実際やってみるとそこまで苦労することもなく、達成することができましたので、初めのうちは細かい設定等々はボイラープレートに任せて、少しずつやっていくとうまく学んでいけそうです。

アドベントカレンダー、明日は[mogya](http://qiita.com/mogya)さんの順番です。