Vue.jsのテストでコンポーネントをいい感じにwrapする方法

0pt   2018-07-12 09:56
IT技術情報局

Vue-test-utilsのshallowMountとmountの違いについて

この記事では Vue-test-utils の shallowMount と mount の違いについてをメインに記事にしていきます。また shallowMount + stubs についても書いていきたいと思います。 Component.methods とコンポーネントから直接 methods を呼び出す方法もあるのでよかったら見ていってください。

  • mount
  • shallowMount
  • shallowMount + stubs
  • TodoContainer.methods

このあたりでを書いていきたいと思います。

個人的には「Vue-test-utils」には、Vue.jsのコンポーネントとかをいい感じにwrapしてくれて、jestとかのテストランナーに乗せれるようにしてくれるいい感じのヤツという認識があります。(訂正とか正確な表現があればコメント欄なりPRお願いします。)

Vue-test-utilsがどんなものか、どういう感じで誕生したかは、以下の記事に詳しいかと思います。 https://qiita.com/re-fort/items/63bef6778bf3d3939815

ドキュメントは https://vue-test-utils.vuejs.org/ja/ こちらになります。今回の内容は、ドキュメントに書いてあることがほとんどなので、私の記事からテストに入った方もぜひドキュメントを読んでみてください。このドキュメントは長くないので流し読みなら1時間もあれば読めると思います。

はじめに

こんにちは。僕です。

前回は 【Vue.js】いつから「フロントエンド開発でTDDができない」と錯覚していた? ※1 という記事を書かせていただきました。。フロントエンド界隈ってまったくテストが盛り上がっていなくて、ましてや「弊社TDDやってます」なんという声とかはほとんどあがっていない状況です。(私はそう感じているという主観的な意見ですが。)

※1 タイトルの元ネタ BLEACHの藍染惣右介のセリフより

「フロントエンドのテストって難しくないよ!!」とか「フロントエンドのロジック部分はTDDできるよ!!」って伝えたくて記事を書かせていただきました。基本的にVueのテストについての記事はQiitaに書いていくので、QiitaかTwitterをフォローしてくれると情報が早いかと思います。

本文

前回のコンポーネントの復習

前回の 記事 を読んでいないかたはぜひ読んできてください。同じコンポーネントを使うことにします。

68747470733a2f2f71696974612d696d6167652d

$ git clone https://github.com/ykhirao/vue-todos.git src/components/TodoContainer.vue <template> @createTodo="addTodo" > v-for="todo in todos" :id="todo.id" :key="todo.id" :text="todo.text" :checked="todo.checked" @toggleChecked="toggleChecked" > </template> <script> import NewTodo from "@/components/NewTodo.vue" import TodoItem from '@/components/TodoItem.vue' export default { name: "todo-container", components: { NewTodo, TodoItem }, data() { return { todos: [] } }, methods: { addTodo(text) { this.todos.push({text, checked:false, id: this.todos.length + 1 }) }, toggleChecked(id) { const todo = this.todos.find(x => x.id === id) todo.checked = !todo.checked } } } </script> src/components/TodoItem.vue <template> type="radio" class="radio" :checked="checked" @click="toggleChecked" > {{text}} </template> <script> export default { name: "todo-item", props: { text: { type: String, required: true }, checked: { type: Boolean, required: true }, id: { type: Number, required: true } }, methods: { toggleChecked() { this.$emit("toggleChecked", this.id) } } } </script> src/components/NewTodo.vue <template> class="new" v-model="text" @keyup.enter="submit" > </template> <script> export default { name: "new-todo", data() { return { text: "" } }, methods: { submit() { this.$emit("createTodo", this.text) } } } </script> htmlの比較

さて

html methodについて

shallowMountのhtml()

tests/unit/TodoContainer.spec.js it.only("toggleChecked", () => { const wrapper = shallowMount(TodoContainer, { data() { return { todos: [{ id: 1, text: "text", checked: true }] } } }) console.log(wrapper.html()) }) console.log.html

mountのhtml()

tests/unit/TodoContainer.spec.js it.only("toggleChecked", () => { const wrapper = mount(TodoContainer, { data() { return { todos: [{ id: 1, text: "text", checked: true }] } } }) console.log(wrapper.html()) }) console.log.html class="new"> type="radio" class="radio"> text </div>

https://github.com/vuejs/vue-test-utils/issues/773

結論

mount の方はコンポーネントすべてをレンダリングしてくれるのですごく便利だったりします。
ただ大規模なコンポーネントになるとstubで表示してくれる shallowMount の方が便利だったりします。

またshallowMount + stub という書き方もあるので、それも紹介します。

shallowMountのhtml + stubで書く方法 it("shallowMount + stubs", () => { const wrapper = shallowMount(TodoContainer, { stubs: { NewTodo: "NewTodo", TodoItem: "" } }) wrapper.setData({ todos: [ { id: 1, text: "text", checked: false } ] }) console.log(wrapper.html()) })

以下のように出力されます。特にコンポーネントをレンダリングする必要ないときは自分は

NewTodo

もしくは

でstub を作ったりします。 NewTodo data-test="todo" id="1"> </div>

また

みたいに :id="id" みたいなv-bindもきちんとレンダリングされるのである程度しっかりした検証もできます。 Wrapしない方法

実はwrapしなくてもテストがかけたりします。

import TodoContainer from "@/components/TodoContainer.vue" it.only("Methodsのテスト", () => { console.log(TodoContainer.methods) })

import したコンポーネントから methods でアクセスできます。

{ addTodo: [Function: addTodo], toggleChecked: [Function: toggleChecked] }

メソッドの検証でwrapが必要ないときに使ったりします。

methods: { addTodo(text) { this.todos.push({text, checked:false, id: this.todos.length + 1 }) } } it.only("Methodsのテスト", () => { console.log(TodoContainer.methods) // { addTodo: [Function: addTodo], // toggleChecked: [Function: toggleChecked] } const localThis = { todos: [] } console.log(localThis) // { todos: [] } TodoContainer.methods.addTodo.call(localThis, "test") console.log(localThis) // { todos: [ { text: 'test', checked: false, id: 1 } ] } })

addTodo を呼び出したときに this のスコープが変わるので、 call でテストように this を渡してあげていますが、それ以外は難しい処理をしていないと思います。

このやり方は結構特殊であまり記事はないので console.log しまくって あ、これでアクセスできるんだ 的にやっていってほしいのですが もしcreated, mountedで重い処理がある場合、マウントせずにメソッドを呼び出したい時 であれば使えばいいと思います。

他の例はまだ作成中ですが vue-testing-handbook # testing-emitted-events を見ていただけるといいかと思います。

余談

bind系メソッドがhtmlレンダリングされない件

現状Vue-test-utilsのhtml()メソッドで:bind系の要素が上手くレンダリングされないみたいです。

tests/unit/TodoContainer.spec.js it.only("toggleChecked", () => { const wrapper = mount(TodoContainer, { data() { return { todos: [{ id: 1, text: "text", checked: true }] } } }) console.log(wrapper.html()) }) console.log.html class="new"> type="radio" class="radio"> --> text </div>

Vue-test-utilsのIssue に記載しておきましたので、そのうち修正されるかもしれません。そのうち。また仮にこのfeatureがマージされると snapshot-test がたくさん更新されるはずなので、その時はsnapshotだけのPR作らないとね、という話で盛り上がりました。

まとめ

  • mount は子コンポーネントの html をマウントしてくれる
  • shallowMount は子コンポーネントの html をマウントしない
  • shallowMount + stubs は子コンポーネントの html をstubでマウントできる
  • TodoContainer.methods でメソッドだけのテストは意外に便利

終わりに

いい感じにwrapできるようになりましたか?いい感じにwrapができるようになればVue-test-utilsマスターになれると思いますよ◎

Source: Javascriptタグが付けられた新着投稿

   ITアンテナトップページへ
情報処理/ITの話題が沢山。