小さく始めるVue.jsユニットテスト環境

## はじめに

Vue.jsにおけるユニットテストの情報源としてはじめに参照すべきなのは公式ドキュメントです。

[単体テスト - Vue.js](https://jp.vuejs.org/v2/guide/unit-testing.html)

ここでは、セットアップ方法についてはテストツールのドキュメントに委譲しています。

> 詳しいセットアップについては、各テストツールのドキュメントを確認して下さい。

モダンなJavaScript開発の常として、複数のツールを組み合わせた環境のセットアップが必要になります。

ゼロから作るアプリケーションなら、[vue-cli](https://github.com/vuejs/vue-cli)で`vue init webpack`すれば、テスト周りのセットアップもやってくるので問題ありません。しかし、既存の（テストのない）JavaScriptアプリケーションにVue.jsを導入しようとしている場合、このアプローチは取れません。
本記事では、Vue.jsのユニットテスト環境をゼロから構築する方法を解説します。

なお、本記事のコードは下記GitHubリポジトリでも公開しています。
https://github.com/ryo-utsunomiya/vue-unit-test-from-scratch

## Vue.jsのセットアップ

手始めに、Vue.jsの実行環境を構築します。Node.jsとnpmをインストール済みであることを前提にします。また、[単一ファイルコンポーネント](https://jp.vuejs.org/v2/guide/single-file-components.html)をコンパイルできるようにすることを目標にします。

### ライブラリのインストール

Vue本体と、ビルド用ライブラリ一式をインストールします。

```bash
npm init
npm i vue
npm i -D babel-cli babel-preset-env webpack babel-loader css-loader vue-template-compiler vue-loader
```

### ビルド設定

はじめにbabelの設定を行います。`.babelrc`というファイルを作成し、以下のように記述してください。

```json
{
  "presets": [
    "env"
  ]
}
```

次に、webpackの設定を行います。`webpack.config.js`というファイルを作成し、以下のように記述してください。

```js
const path = require('path');

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'build.js',
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
    },
  },
};
```

ここでは、`src/main.js`というファイルをエントリーポイントとし、`dist`というディレクトリにビルド結果(build.js)を出力しています。また、拡張子が「.vue」のファイルはvue-loaderで、「.js」のファイルはbabel-loaderで処理します。

### ソースコードの記述

srcディレクトリを作成し、その下に`main.js`と`App.vue`を配置します。

```
src
├── App.vue
└── main.js
```

`main.js`は以下のように記述します。

```js
import Vue from 'vue';
import App from './App.vue';

new Vue({
  el: '#app',
  render: h => h(App),
});
```

`App.vue`は以下のように記述します。

```html
<template>
  <div>{{ message }}</div>
</template>
<script>
export default {
  data() {
    return {
      message: 'Hello Vue!',
    };
  },
};
</script>
```

次に、ビルドスクリプトを用意します。package.jsonのscriptsを以下のように変更してください。

```
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
```

追加したのは`build`で、webpackを呼び出すようにしています。

この状態で、`npm run build`コマンドを実行すると、ビルドに成功するはずです。

この段階では画面が確認できず、Vueアプリケーションがちゃんと動いているか不安になるので、動作確認をしましょう。index.htmlを以下の内容で作成します。

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Vue unit test from scratch</title>
</head>
<body>
  <div id="app"></div>
  <script src="/dist/build.js"></script>
</body>
</html>
```

次に、開発用Webサーバーを立てます。開発用サーバの立て方がわかっている方は、この手順は飛ばしても構いません。手元のマシンがMacであれば、`php -S localhost:8080`して http://localhost:8080/ にアクセスするといった方法でもOKです。本記事では、Nodeで完結させるため、追加のライブラリをインストールします。

```
npm i -D webpack-dev-server
```

インストールしたら、package.jsonのscriptsを以下のように書き換えます。

```
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "dev": "webpack-dev-server --open"
  },
```

`npm run dev`コマンドを叩くと、Webpackのビルドが走り、ビルド後にサーバが立ち上がって、ブラウザが http://localhost:8080/ を開きます。

## テストのセットアップ

ここからようやく本題に入ります。今回は、[vuejs-templates/webpack](https://github.com/vuejs-templates/webpack)を参考に、Karma + mochaの組み合わせでユニットテスト環境を作ります。また、ひとまず最小構成で動かしたいので、ブラウザはChromeでいきます（CIで動かしたい場合などはPhantomJS等を使うことになると思います）。

はじめに、アサーションライブラリのmochaと、テストランナーのkarmaをインストールします。

```
npm i -D mocha karma karma-chrome-launcher karma-mocha karma-webpack
```

次に、`karma.conf.js`を作成します。

```js
const webpackConfig = require('./webpack.config');

module.exports = function(config) {
  config.set({
    frameworks: ['mocha'],
    files: [
      'test/**/*.spec.js'
    ],
    preprocessors: {
      'test/**/*.spec.js': ['webpack'],
    },
    webpack: webpackConfig,
    reporters: ['progress'],
    browsers: ['Chrome'],
  });
};
```

ここでは、以下のような設定を行っています。

- アサーションはmochaを使用
- テストは testディレクトリ配下に *.spec.js という名前で置く
- webpackによるビルド後にテストを実行する
- テストレポートはprogressレポーターを使用する
- ブラウザはChromeを使用する

設定ができたら、次はテストの記述です。testディレクトリを作成し、その下にApp.spec.jsを作成してください。内容は以下です。

```js
import Vue from 'vue'
import App from '../src/App.vue'
import assert from 'assert';

Vue.config.productionTip = false;

describe('App', () => {
  it('default data', () => {
    assert.equal('function', typeof App.data);
    const defaultData = App.data();
    assert.equal('Hello Vue!', defaultData.message);
  });

  it('mount component', () => {
    const vm = new Vue(App).$mount();
    assert.equal('Hello Vue!', vm.message);
  });

  it('renders the correct message', () => {
    const Ctor = Vue.extend(App)
    const vm = new Ctor().$mount()
    assert.equal('Hello Vue!', vm.$el.textContent);
  });
});
```

最後に、karmaを起動するためのコマンドを書きます。package.jsonのscriptsを以下のように書き換えてください。

```
  "scripts": {
    "test": "karma start --single-run",
    "build": "webpack",
    "dev": "webpack-dev-server --open"
  },
```

これで、`npm run test`（または`npm test`、`npm t`）でkarmaを起動できるようになります。

`npm t`を実行すると、webpackのビルド => Chromeの起動 => テスト結果の出力の順で動作するはずです。

最終的な結果は以下のように出力されます。

<img width="789" alt="スクリーンショット 2017-10-21 12.40.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/33450/086cf097-d47d-f833-745e-74bb4b91dcf3.png">

**Chrome 61.0.3163 (Mac OS X 10.13.0): Executed 3 of 3 SUCCESS (0.012 secs / 0.006 secs)** が最終的なテスト結果です（ここでは、3件のテストが全て成功しています）。

## 終わりに

ここまでやれば、Vueコンポーネントのテストを書くのに最低限必要な環境は整ったといえます。
この先、環境を拡張していく上で参考になるのは、[vuejs-templates/webpack](https://github.com/vuejs-templates/webpack)でしょう。ここには、ヘッドレスブラウザ(PhantomJS)によるテストの実行、Sinon.jsによるモックの利用、テストのカバレッジレポートの出力等、本記事では触れなかった機能が盛り込まれています。