Nuxtを利用したVueアプリのユニットテストに関するメモ

今回のゴール

Nuxt.js を利用した Universal な Vue + Vuex アプリのユニットテストをできるようになる。

Nuxt.js についてはユニットテストに関する情報が全然出てこないが、@vue/test-utils のドキュメントを順番に読んでいけば基本的には大丈夫。ただし追加の設定ファイルの作成などは必要。

以下はそのための断片的なメモ。一応完全に動いているサンプルリポジトリはできているので、細かいコードはそっちを参照のこと。

まとめのサンプルリポジトリ

wtnabe/tdd-bed-nuxt-mocha-webpack-power-assert: A minimum base for TDD practice with Node.js, Vue.js, Nuxt.js, mocha-webpack, and PowerAssert

材料

設定ファイルは追加が必要

  • Nuxt 自体は設定を隠蔽するがテスト支援はない
    • Vue.js としてのテストを行おうとする場合、テスト用の各種設定は別途用意する必要がある(ex, webpack.config.js など)
  • Nuxt 公式のサイトではテストについては E2E の記述しかないのでユニットテストについては別途考える必要あり
    • 理由は後述

テストランナーはこれまでの経験からmocha-webpackを選択

テストランナーについては

  • Vue.js 公式は Karma
  • Nuxt 公式は Ava with jsdom

が推されているが、これまで自分は mocha + power-assert の環境を作るようにしてきていたので、今回もこれをベースにすることにした。一回に挑戦は一つまでの法則。

テスト用のwebpack.config.jsを用意

Nuxt 自体は Webpack の設定は動的に生成してさらに nuxt.config.js の内容を加えて動作をするが、Nuxt の外からユニットテストを行う際にこの設定を引っ張ってくるのは難しい。そこで必要最低限の webpack.config.js を用意する。

今回は

test/webpack.config.js

を用意することにした。

aliasが必要

Nuxt ではディレクトリレイアウトにルールがあり、このルールに従ったアプリ開発の際に便利なように @, @@, ~, ~~ に alias が用意されている。

ディレクトリ構造 - Nuxt.js

この alias をテストの際に再現しておかないと component の import すらできないので以下のように設定を用意する。

..
  resolve: {
    alias: {
      '@': path.resolve(__dirname, '../'),
      '~': path.resolve(__dirname, '../')
    }
  },
..

.vueを解釈するloader設定を追加

 module: {
   rules: [
     {
       test: /\.vue$/,
       loader: 'vue-loader'

Babelを通ってないとES2016とか動かないのでそれも追加

{
  test: /\.js$/,
  loader: 'babel-loader'
}

..babelrc

{
  "presets": ["vue-app"]
}

が必要。

※ Nuxt の Babel の設定は vue-app になっている。これは vue + preset-env に相当する。

対応していない import は空 object になる

試しに yaml-loader なしに component の中で YAML を読み込む記述があった場合は

  • ファイルが見つからなければエラーになる
  • ファイルが見つかって対応している loader がない場合 {}
  • ファイルも対応している loader もある場合、通常通り

少なくとも vue-loader, babel-loader 以外を最初からすごく気にする必要はなさそう。

window objectを再現するためにjsdomをglobalに

window object がないと Vue component の renderering の際に正常に動作しないので以下のように必要なパッケージを追加

$ yarn add --dev jsdom jsdom-global

して、これを常に有効にするために以下のようにファイルを用意し、

test/setup.js

require('jsdom-global')()

テスト実行時に読み込む。

$ mocha-webpack --require test/setup.js

もうassertはpower-assertでいいです

global.assert = require('power-assert')

CoffeeScript と組み合わせていた時はどのタイミングで power-assert を解釈できるようにするかいろいろ工夫が必要だったけど、「全部 Babel 通せばええやん」の世界になったおかげか、単に require するだけで利用できるようになっていた。楽だ。

componentをtest-utilsでmountする

Vue アプリも実際に動作させるために mount を行うわけだけど、これを @vue/test-utils で再現する。

実は素の component をテストするだけなら別に直接叩いてもいいんだけど、以下の理由により必ず test-utils で mount した方がいいです。

  • reactive system や render した結果をテストしやすい
  • store など周辺の環境も再現できる

※ この際、本来の利用方法のまま、インスタンス化する前の options オブジェクトを mount する。new Vue() とかしちゃダメ。

test-utilsがなくてもtestableなコードを目指すとよさそう

test-utils 確かに shallow は便利。

また mount / shallow で setProps などで外からデータを与えることができるので、どのような状態もまず再現できる。ただし、option を object で直接組み立てていくのはちょっとイマイチな感じがする。stub out しにくいし、まるっと置き換わってしまうので、同じ component を組み立てているとは限らないし、ちょっと @vue/test-utils ありきすぎる感じがする。

考えたら単に import の処理などを computed などにしてメソッドの stub out するだけでもなんとかなる。この辺は test-utils そのものに詳しくなくても書けるような、標準的なというか一般的なというか、testable code を目指して書いていけばよいような気がする。

Vuex Storeは基本的にただのfunctionなのでTDDで始めやすい

mutation は引数に stateを受け取るので、最も素朴な Store の実装の場合、mutations しか定義できてなくても、Vuex Store を new しなくてもテストは可能である。

export const state = () => {
  return {
    ..
  }
}

export const mutations = {
  method(state) {
    ..
  }
}

こんな感じで個々の mutation には必ず state が渡ってくるので。

もちろんその場合は commit() は使えないし、結果の state を直接覗いてテストすることになる。

(個人的にはこの Vuex Store の構造は TDD でスタートするのにとても向いている気がする。この構造で自信を持てたと言っていい。)

あと気をつけるところは、

  • Vuex インスタンスを生成するには通常の利用と同じく Vue.use が必要
  • テストの安定性、独立性という意味では global な use でいいかどうか迷う
    • 迷う場合は createLocalVue から

ただし、gettersを引き回したりする場合にはVuexインスタンスが必要

getters や mutations には state しか渡ってこない。しかし mutation 内で getters を呼びたい場合もあると思う。その場合は

export const mutations = {
  method(state, {arg, getters}) {
    ..
  }
}

のようなコードが必要になるのだが、ここで getters はちゃんと Store インスタンス生成後の store.getters を渡すようにしてあげないと挙動が変わってしまう。(しばらく悩んだ)

SSRの本当のテストにはE2Eが必要(だけど未検証)

jsdom を前提にして component のテストと store のテストをするだけなら上のやり方で十分なんだけど、Nuxt は Universal アプリを作れるので、実はこれだけでは不十分な場合がある。

例えば生 DOM に依存する component を使っていたり、非同期処理が component mount 前に済んでいなかったりすると SSR には失敗するのだが、これは component 単体のテストではガードできない。

しかしこれはこのエントリの範囲外なので各位頑張れ。

Evoのムービーを扱う

モノ

  • Evo をメモリーカードモードにして SD から引っこ抜いた
  • Evo で撮影したムービーは 3gpp フォーマットらしい。そらそうか。拡張子は .3gp
  • 3gpp の対応コーデックは
    • 映像 H.263(必須), MPEG4, H.264(オプション)
    • 音声 AMR, MPEG-4 AAC

再生

Mac 上ではとりあえず

  • QuickTime で普通に再生できる

編集

  • そのままだと iMovie では開けない
  • 拡張子を .mov に変えれば iMovie でも扱える

メタデータ

  • USB接続できるカメラから転送で iMovie に持って来るわけではないのでメタデータは何もない
  • そもそもムービーのデータは Exif のようにメタデータの規格が決まってない

ということで潔く諦める。すぐに処理できる範囲内の短いムービー以外は基本的に撮っちゃダメだと思う。

emacs で開いてる buffer の改行コードを変える

バカすぎた。こんなことに気づいてなかった。

今まで

M-x set-buffer-file-coding-system

で文字コードは何回変更したか分からない。でもこのとき改行コードまで変更できることに全然気づいてなかった。

Coding system for saving file (default nil):

って聞かれたときに今までだと

sjis

とか

utf-8

とか入れてたんだけど、これを

sjis-dos

とかにすれば改行コードを変えられたということに twitter で reply をもらうまで気づいていませんでしたorz

以下はそのときの twitter のログ。

12:55:37 wtnabe< emacs で開いてるバッファの改行コードって変えられないの
かな
13:19:49 mf2t> @wtnabe C-x Ret f で *-unix にするとかじゃなくて?
13:22:49 kyanagi> @wtnabe C-x RET f とかでしょうか? < 改行コード
14:27:56 wtnabe< @kyanagi @mf2t 文字コードしか変えられないと思ってまし
た。。。お恥ずかしい。

なーにをやってんだオレ。

ムービー系経験不足

RD からネット de ダビングで転送して、再エンコードして容量を小さくしようと思ったんだけど、この手のツールは経験値が低いのでなかなか苦戦している。

最終目的地は OS X 経由でファイルサーバか DVD や SVCD 辺り。(あ、うちの機械は DVD 焼けないな。)以下、やってみて分かったこと。登場するツールと成否のパターンはこんな感じ。

ツール成否
vrx 0.6×
RDService for Windows 2.0 β7
IP messenger(Win 2.06 + OS X 0.9)×
OS X の sshd 3.6.1p1 + WinSCP(バージョン忘れた)
OS X の tnftpd 20040810 + Win2000 の Explorer×
ffmpegX 0.0.9s(と、そのエンジンたち)1

ダビング

  • vrx(with 無線)が、受けるデータが 500MB を超えたくらいで切れる(別な日にやって再現したので間違いないと思う)
    • 最初はダメルコ HUB を疑ったんだけど、どうもそうじゃなかった
    • vrx そのものは落ちないんだけど転送が止まる。vrx 側ではエラーは分からない。RD 上ではエラー。
    • vrx を再起動しないと RD 側でダビング先として認識できなくなったりする
    • 一度転送に失敗した RD は再起動しないと転送が始まりもしなくなる2。つまり上の状態と合わせると、どっちも再起動しろ。
  • RDService for Windows(with 有線)では全部(1GB強)受け取れた

Win → Mac 転送

  • できあがったデータを IP messenger で Win → Mac 転送をしても 500MB くらいで切れる(この辺の容量に何かあるのかな?)
  • scp での転送はイケたけど、WinSCP が日本語のファイル名に対応していない
  • Windows の「ネットワークプレイスの追加」から ftp での転送を試みたがログインすらできず。Windows にまともな ftp クライアントを入れるのも面倒だったのでこれはここまでで終了。

エンコード@Mac

  • ffmpegX は英語環境じゃないと動かない
  • ffmpegX でのエンコード時、エンジンを ffmpeg 以外3にするとオーディオトラックが落ちる

全部 Windows でやればいいんだけど、一応メインマシンは OS X なのでそっちで完結させるのが目標。つまり、一発で OS X にダビングしてそこでエンコードする。DVD に焼くことを考えると Win マシンの方に SuperDrive を載っけてそっちで完結するのがいいんだけど(PowerBook なのでドライブ交換とかする気なし)、まだそこまでは考えてない。RD で(必要ならレート変換ダビングして)焼けばいいんじゃねーのくらいに考えている。

ということで一応目的は達成できるようになったけど、まだまだ最適解ではないのでその辺の詰めが課題。なんていうか、場合分けのパターンが多くて面倒。有線と無線の違い、ネット de ダビングに対応しているソフトが RDService, vrx, vrd2 てな辺りか。(とりあえず一度 Windows アプリは無視しよう。)配線の都合上、完全有線をテストするのは面倒なので、できれば無線でスムーズに行くパターンを見つけたい。おまけにエンコーダにも習熟してない(MPEG1 時代の TMPGEnc しか分からん)のでその辺も調べていかないと。

あー。そもそもそんなに再エンコ → 保存の必要性ってないんだよな。目的と手段が分からなくなってきてるな。

  1. 全然つかみきれてません 

  2. でも Web での予約やダビング先の認識はできる。なんかすごく半端な状態に 

  3. どのエンジンが ok なのか、まだ把握しきれていない 

About

例によって個人のなんちゃらです