この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
おはようございます、加藤です。Vue.jsのユニットテスト環境作成の方法をまとめました。もし、このブログが公開から時間が経っているなら情報が古い可能性が高いです、ご注意ください。
なぜわざわざこんな事を言うかというと、私がこのブログを書いた理由は簡単に環境作成できるにも関わらず古い情報にぶつかって無駄に時間を溶かしたからです。。。
tl;dr
- Vue.jsのユニットテストの導入方法
- マウンティングオプションは
mount
とshallowMount
どちらを使うべきか - 見るべきドキュメント
環境作成までがメインでユニットテストの作成方法についてはどのドキュメントを何の為に読んだかをまとめています。
ブログを書く経緯
最近Vue.jsの基礎を勉強したので自主4連休中に、Techpitで販売されているVue.js/Vuexを使ってTrello風アプリを作成しよう!を買ってサンプルアプリを作っていました。
基礎を学んだのだから教材無しで作れると言えば作れるのですが、勢いを付けたい・テーマ考えるのが面倒・初手は楽をしたいという事でお金で解決しました。
この教材や検索するとヒットするVue.jsの情報はJavaScriptで書かれているのがほとんどです。私は最近TypeScriptを使い始めたのでTypeScriptで書きたいのですが、 Vue.js × TypeScript という慣れていない事同士を同時に進めるのは効率が悪いので一旦はその情報に従いJavaScriptでVue.jsを書いてきました。
Vue.jsも初学者程度には身についたので、今まで書いたVue.jsをTypeScriptに置き換える所から始めたいのですが、こういった改修を行う際には、事前にユニットテストを書くべきなので書き方を調べました。また、CompositionAPIを導入する際にも役立つはずです。
環境
下記のバージョンを対象としています。
- Vue 2.6.11
- VueCLI 4.1.2
ユニットテスト環境作成
ユニットテスト作成の方法は非常に簡単です。vue create
する際に、featureでUnitを有効にしていれば最初からセットアップされています。後から追加したくなった場合は下記のコマンドを実行すればOKです。
vue add @vue/unit-jest
後はテストを書くだけ!ものすごく簡単ですね。
Vue CLIはv3以降からプロジェクト作成開始時にテンプレートをダウンロードする方式から、オプションとして後から追加もできるように変更された様です。(ちゃんと調べていないです。。。ツッコミあればTwitterにコメントをお願いします)
私が古い情報にぶち当たってしまったのは、こういった経緯が原因です。
マウンティングオプションは mount
と shallowMount
どちらを使うべきか
Vue.jsのテストはコンポーネント単位でHTMLを描画し、意図された描画が行われているというテストをします。
mount
と shallowMount
この2つの違いは子コンポーネントを実装どおりに描画するか、スタブするかです。
マウンティングオプション | 子コンポーネントの描画 |
---|---|
mount | 実装どおり |
shallowMount | スタブ |
図の様なカンバン方式の構造をイメージしてください。
これの親子関係は下記となります。
Board
├── List
│ ├── Card
│ └── CardAdd
└── ListAdd
これを前提として、 List
を mount
でマウントすると配下の Card
, CardAdd
も、実装に沿って描画されます。つまりテストパラメータに従ってこれら子コンポーネントの描画も変化します。対して shallowMount
の場合はスタブに置き換えが行われ、テストパラメータに関わらず常に一定です。
shallowMount
を使うことで、 List
をテストする際に List
に集中する事ができ好ましいです、子コンポーネントに対するテストは別のユニットテストとして行うべきです。
ただし、小規模なプロジェクトであれば、親コンポーネントから mount
を使って一気に描画してしまいテストを行うという選択肢もありだと考えています。ここらへんはケース・バイ・ケースで調整するしかなさそうです。
ちなみに shallow
は調べてみると浅いという意味でした。仕組みを踏まえると納得ですね。
サンプルとして、List.vueとそれに対するHTML描画の違いを載せて起きます。
List.vue
<template>
<div class="list">
<div class="list-header">
<p class="list-title">{{title}}</p>
<p class="list-counter">total: {{totalCardInList}}</p>
<div class="delete-list" @click="removeList">x</div>
</div>
<draggable group="cards"
:list="cards"
@end="$emit('change')"
>
<Card v-for="(item, index) in cards"
:body="item.body"
:key="item.id"
:cardIndex="index"
:listIndex="listIndex"
></Card>
</draggable>
<cardAdd :listIndex="listIndex"></cardAdd>
</div>
</template>
<script>
import Card from './Card';
import CardAdd from './CardAdd';
import draggable from 'vuedraggable';
export default {
name: "List",
props: {
title: {
type: String,
required: true,
},
cards: {
type: Array,
required: true
},
listIndex: {
type: Number,
required: true,
},
},
computed: {
totalCardInList() {
return this.cards.length;
}
},
methods: {
removeList() {
if (confirm('本当にこのリストを削除しますか?')) {
this.$store.dispatch('removeList', {listIndex: this.listIndex});
}
},
},
components: {
Card,
CardAdd,
draggable
}
};
</script>
mount
<div class="list">
<div class="list-header">
<p class="list-title">test title</p>
<p class="list-counter">total: 0</p>
<div class="delete-list">x</div>
</div>
<draggable-stub list="" clone="function(t){return t}" element="div" group="cards"></draggable-stub>
<cardadd-stub listindex="0"></cardadd-stub>
</div>
shallowMount
<div class="list">
<div class="list-header">
<p class="list-title">test title</p>
<p class="list-counter">total: 0</p>
<div class="delete-list">x</div>
</div>
<div></div>
<form class="add-card"><label><input type="text" placeholder="Add new card" class="text-input"></label>
<!---->
</form>
</div>
見るべきドキュメント
私は、Vue.jsのテストについて知るために、どんな順番で何を調べたか最終的に役立ったものをまとめます。
- ガイドのはじめる〜一般的なヒントを読み全体的なイメージを持つ ガイド | Vue Test Utils
- テスト用のHTML描画する際に data や props の与え方を知る API | Vue Test Utils
- 取得したHTML描画の 何(クラスや文字列など) に対して 何(存在するかなど) をテストする為の方法を知る Wrapper | Vue Test Utils
あとがき
Vue.jsにユニットテストの環境を導入するのは簡単なのですが、古い情報にぶち当たりまくって1時間以上時間を溶かしたので同じ被害に合う方が少しでも減ればと情報をまとめてました。フロントエンドもテストを書いていくぞ!!