Motomichi Works Blog

モトミチワークスブログです。その日学習したことについて書いている日記みたいなものです。

vue-test-utils + jestを追加する | webpack4.x+babel7+vue.js 2.x 環境構築 2019年3月版 ステップ0002

参考にさせて頂いたページ

テストを実行する環境の構築

specファイルの書き方

はじめに

Windowsでもできます。

MacUbuntuなどでもできると思います。

これから何回かに分けてnpm initするところから、以下のようなことをできる環境を構築していきます。

  • webpack4 + babel7 を使ってビルド
  • vue-test-utils + jest を使ってテスト実行
  • eslintを使って .vue と .js の構文チェック

今日の環境

  • Windows10 Home
  • node v8.11.1
  • npm 5.8.0

前提

以下の記事の手順で基本的なビルド環境の構築をしたところまでが前提です。

続きとしてテストを実行できるようにしていきます。

node_modulesを追加する

以下の5つのモジュールを追加する必要があるようです。

Additional Dependencies

vue-test-utils jest babel-jest (for ES2015+ features in tests) vue-jest (for handling *.vue files in tests) jest-serializer-vue (for snapshot tests)

以下のコマンドでインストールしました。

npm install --save-dev @vue/test-utils jest babel-jest vue-jest jest-serializer-vue

package.jsonを編集

package.jsonのscriptsのtestを編集してjestを実行するようにします。

  "scripts": {
    "watch": "webpack -d --watch",
    "test": "jest"
  },

jest.config.jsの作成

windowsの場合

copy nul jest.config.js

macの場合

touch jest.config.js

jest.config.jsの編集

以下のように記述します。

module.exports = {
  // テスト対象の拡張子
  "moduleFileExtensions": [
    "js",
    "vue",
  ],
  // specファイル中の"^@/(.*)$"にマッチする文字列を"<rootDir>/src/$1"のパスに置き換えてテストを実行
  "moduleNameMapper": {
    "^@/(.*)$": "<rootDir>/src/$1",
  },
  // transformerを設定
  "transform": {
    "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
    ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
  },
  "snapshotSerializers": [
    "<rootDir>/node_modules/jest-serializer-vue",
  ],
}

ここまでやった段階でのpackage.json

以下のようになっています。

{
  "name": "webpack_4_vue_introduction",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "watch": "webpack -d --watch",
    "test": "jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.4.0",
    "@babel/preset-env": "^7.4.2",
    "@vue/test-utils": "^1.0.0-beta.29",
    "babel-jest": "^24.5.0",
    "babel-loader": "^8.0.5",
    "jest": "^24.5.0",
    "jest-serializer-vue": "^2.0.2",
    "vue-jest": "^3.0.4",
    "vue-loader": "^15.7.0",
    "vue-template-compiler": "^2.6.10",
    "webpack": "^4.29.6",
    "webpack-cli": "^3.3.0"
  },
  "dependencies": {
    "vue": "^2.6.10",
    "vuex": "^3.1.0"
  }
}

testディレクトリを作成する

mkdir test

空ファイルを二つ作成する

windowsの場合

以下のコマンドで作成できると思います。

copy nul test\TextFieldUnit.spec.js
copy nul test\TextField.spec.js

macの場合

以下のコマンドで作成できると思います。

touch test/TextFieldUnit.spec.js
touch test/TextField.spec.js

./test/TextFieldUnit.spec.jsの作成とその記述内容

import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
// modules
import textFieldUnit from '@/javascripts/vue_applications/common/modules/textFieldUnit.js'
// components
import TextFieldUnit from '@/javascripts/vue_applications/common/components/TextFieldUnit.vue'

const localVue = createLocalVue()

localVue.use(Vuex)

describe('TextFieldUnit.vue', () => {
  let store;

  beforeEach(() => {
    // 本来のthis.$storeに入るものと同様のオブジェクト構造を作成します。
    store = new Vuex.Store({
      state: {},
      getters: {},
      mutations: {},
      actions: {},
      modules: {
        textFieldUnit,
      },
    })
  })

  it('mutationsの動作確認', () => {
    const state = store.state.textFieldUnit;
    const key = 'count';

    textFieldUnit.mutations.setState(state, {
      key,
      value: state[key] + 1,
    });

    expect(state[key]).toBe(1);
  });

  it('ボタンのclickイベントが発火したとき', () => {
    const wrapper = shallowMount(TextFieldUnit, { store, localVue })
    const button = wrapper.find('button')

    // 初期値の検証
    expect(button.text()).toBe('count: 0')

    // イベント発火
    button.trigger('click')

    // 変更後の値の検証
    expect(button.text()).toBe('count: 1')
  });
})

./test/TextField.spec.jsの作成とその記述内容

import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
// modules
import textFieldUnit from '@/javascripts/vue_applications/common/modules/textFieldUnit.js'
import textField from '@/javascripts/vue_applications/common/modules/textField.js'
// components
import TextField from '@/javascripts/vue_applications/common/components/TextField.vue'

const localVue = createLocalVue()

localVue.use(Vuex)

describe('TextField.vue', () => {
  let store;

  beforeEach(() => {
    // 本来のthis.$storeに入るものと同様のオブジェクト構造を作成します。
    store = new Vuex.Store({
      state: {},
      getters: {},
      mutations: {},
      actions: {},
      modules: {
        textFieldUnit,
      },
    });
  });

  it('mutationsの動作確認', () => {
    const state = store.state.textFieldUnit.textField;
    const key = 'value';

    textFieldUnit.mutations.setState(state, {
      key,
      value: 'editedValue',
    });

    expect(state[key]).toBe('editedValue');
  });

  it('テキストフィールドのinputがイベント発火したとき', () => {
    const wrapper = shallowMount(TextField, { store, localVue });
    const input = wrapper.find('input');
    const syncedText = wrapper.find('.test-synced-text');

    // 初期値の検証
    expect(input.element.value).toBe('defaultValue');
    expect(syncedText.text()).toBe('value: defaultValue');

    // イベント発火
    input.element.value = 'editedValue';
    input.trigger('input');

    // 変更後の値の検証
    expect(input.element.value).toBe('editedValue');
    expect(syncedText.text()).toBe('value: editedValue');
  });
});

testを実行してみる

package.jsonのscriptsに定義してあるので、以下のコマンドで実行できます。

npm run test

scoped packagesになったバージョンの@babel/babel-coreを使っているため以下のようなメッセージが出てテスト実行に失敗します。

  ● Test suite failed to run

    Cannot find module 'babel-core'

      at Object.<anonymous> (node_modules/vue-jest/lib/compilers/babel-compiler.js:1:15)

babel-bridge(@babel/coreへの橋渡し)

GitHub - babel/babel-bridge: A placeholder package that bridges babel-core to @babel/core.

さきほどのエラーでは Cannot find module 'babel-core' でした。
'babel-core' を探しにいったときに '@babel/core' へ橋渡しをできるようにします。

npm install --save-dev babel-core@^7.0.0-bridge.0 @babel/core

もう一度testを実行してみる

もう一度testを実行してみます。

npm run test

以下のように4つ成功したかと思います。

Test Suites: 2 passed, 2 total
Tests:       4 passed, 4 total

ここまでやった段階のpackage.jsonの記述内容

{
  "name": "webpack_4_vue_introduction",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "watch": "webpack -d --watch",
    "test": "jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.4.0",
    "@babel/preset-env": "^7.4.2",
    "@vue/test-utils": "^1.0.0-beta.29",
    "babel-core": "^7.0.0-bridge.0",
    "babel-jest": "^24.5.0",
    "babel-loader": "^8.0.5",
    "jest": "^24.5.0",
    "jest-serializer-vue": "^2.0.2",
    "vue-jest": "^3.0.4",
    "vue-loader": "^15.7.0",
    "vue-template-compiler": "^2.6.10",
    "webpack": "^4.29.6",
    "webpack-cli": "^3.3.0"
  },
  "dependencies": {
    "vue": "^2.6.10",
    "vuex": "^3.1.0"
  }
}

おまけ

以下のような感じでvmのcomputedやmethodsにもアクセスできます。

wrapper.vm.hoge

おわりに

testディレクトリに入っている.spec.jsファイルはもっとディレクトリ階層を深くしてもテストを実行できます。

以下のようなテストが実行できるようになりました。

  • Vueコンポーネントのテスト
  • Vuexのstoreのテスト
  • Vueとは関係なく定義した関数のテスト

今日はここまで。

次回は.vueの中にstyleを書けるようにしていこうと思います。

続きはこちら

motomichi-works.hatenablog.com