Edited at

nuxt.js の Unit Test のツラミについて

More than 1 year has passed since last update.

これ、みんなどうやっているのか聞きたいので投稿してみる。

もっといい方法が存在するのであれば教えてください。


nuxt.js は SSR によるアプリケーション開発における必要なものが大体揃っており、良いなーということで使い始めてはいるのですが、やはり、まだまだRC版ということで課題もあります。今回は Unit テストで詰まりました。

nuxt.js では、nuxt-build により store をまとめたり、nuxt.js がプリセットで用意している component を .nuxt/ ディレクトリに用用意する。さらに面倒なことに、.nuxt で展開されているものは、webpackで使う前提となっています。 内部的には、 require.context を利用しているためです。

nuxt.js 公式では、 ava による e2e テストの方法について公式で述べているものの、Unitテストについてはイマイチ情報が少ないです。 Unit testing Issue #461 nuxt/nuxt.js あたりも参照のこと。

// 上記の e2e のテストについても毎回 build が走ってしまうので、個人的には気に食わない。

ということで、自分なりにテスト方法について模索してみました。


前提

ここでは、vue-init webpack によりデフォルトでオススメされる、jest をテストツールとして使います。


導入

yarn で必要なものを入れていく

yarn add -D babel-core babel-jest babel-plugin-dynamic-import-node \

babel-plugin-transform-es2015-modules-commonjs \
babel-plugin-transform-runtime babel-preset-env \
babel-preset-stage-2 babel-register jest jest-serializer-vue vue-jest

必要なファイルを変更したり、作っていく作業を行う


.babelrc

{

"presets": [
["env", {
"modules": false
}],
"stage-2"
],
"plugins": ["transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
}


.eslintrc.js

module.exports = {

env: {
jest: true // jest を true にする
}
}


test/unit/jest.conf.js

const path = require('path')

module.exports = {
rootDir: path.resolve(__dirname, '../../'),
moduleFileExtensions: [
'js',
'json',
'vue'
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/$1'
},
transform: {
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
'.*\\.(vue)$': '<rootDir>/node_modules/vue-jest'
},
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
setupFiles: ['<rootDir>/test/unit/setup'],
mapCoverage: true,
coverageDirectory: '<rootDir>/test/unit/coverage',
collectCoverageFrom: [
'components/**/*.{js,vue}'
]
}



test/unit/setup.js

import Vue from 'vue'

Vue.config.productionTip = false


テスト記述


test/unit/specs/components/AppHeader.spec.js

import Vue from 'vue'

import Vuex from 'vuex'
import VueRouter from 'vue-router'
import VueI18n from 'vue-i18n'
import AppHeader from '@/components/AppHeader.vue'

const store = require('@/store/')

Vue.use(VueRouter)
Vue.use(Vuex)
Vue.use(VueI18n)

describe('AppHeader.vue', () => {
it('pass', () => {
AppHeader.components = {}

// nuxt-link component を使っている場合は、モックを載せる
// この中身は、.nuxt/components/nuxt-link.js にも出来ているので、
// ビルドが行われている前提であれば、そちらを呼んでも良い。
AppHeader.components.NuxtLink = {
name: 'nuxt-link',
functional: true,
render (h, {data, children}) {
return h('router-link', data, children)
}
}

const Constructor = Vue.extend(AppHeader)

// VueI18n を使っているので、載せる。
// 但しUnitレベルでは別にデータは不要なので、silentTranslationWarn: true にして警告を消す
const vm = new Constructor({router: new VueRouter(), i18n: new VueI18n({silentTranslationWarn: true})})

// store/index.js (Module Mode) から store を作る作業
vm.$store = new Vuex.Store(Object.assign(store, {
state: store.state()
}))
vm.$mount()

// テストを記述する
})
})



テスト実行

yarn run から実行できるようにする


package.json

{

"scripts": {
"unit": "jest --config test/unit/jest.conf.js "
}
}

yarn run unit


悩ましいポイント


  • 今のところ、ビルド済みの .vue/ に依存しない形で書いているが、依存させたほうが良いかな?

  • Vuex モジュールが増えてくると面倒なことになりそう。

  • *.vue ファイルのカバレッジが、何故かちゃんと取得できない場合もある。