【Vue.js】【TDD】ブラウザだけでVueテストを書きましょう

基本的に大きなVue.jsアプリケーションを開発するとVue-cliで新しいプロジェクトを作成して、Babel、Jest、Webpackなどを入れます。複雑ですね。でもツールは全部JavaScriptなので、ブラウザだけでも使えます。

この記事で、Node.js、Webpackなどの複雑なツールなしで`vue-test-utils`を使ってTDDで簡単なアプリケーションを作ります。ブラウザとスクリプトタグだけで書きます。　

## 何を作る？

これは、MUST-DOアプリケーションです。TODOのようなアプリケーションですが、TODOを入れられないし、削除できないのでMUST-DOです。

[ソースコードはここ](https://gist.github.com/lmiller1990/566afd59702794c5a0942981cd26177d)。`index.html`だけです。

Vueとテストを書くには必要なものは：

1. Vue
2. Vue-test-utils
3. テストランナー （Mocha.jsを使います）

始めます。

## 検証は？

簡単なアプリケーションなので複雑な検証ライブラリーを使わないです。その代わりに、この関数を使います。

```js
function assert(actual, expected) {
  if (actual != expected) {
    throw Error("やばい")
  }
}
```

```js
assert(1, 2) // => false
assert("A", "A") // => true
```

このテンプレートで始めます。

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
  <script src="https://cdn.rawgit.com/vuejs/vue/dev/dist/vue.min.js"></script>
  <script src="https://cdn.rawgit.com/vuejs/vue/dev/packages/vue-template-compiler/browser.js"></script>
  <script src="https://cdn.rawgit.com/vuejs/vue-test-utils/dev/packages/test-utils/dist/vue-test-utils.iife.js"></script>
  <link href="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css" rel="stylesheet" />
  <script src="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"></script>
  <script>mocha.setup('bdd')</script>

</head>
<body>
  <div id="mocha"></div>

  <script>
    mocha.run()
  </script>
</body>
</html>
```

`script`が５個くらいがあります。

1. Vue
2. Vue template compiler （`vue-test-utils`がこれを使う)
3. vue test utils （Vueコンポーネントを簡単にテスト）
4. mocha.js （テストランナー)
5. mocha.css （Mocha.jsの結果をいい感じにレンダー）

## 条件

２つのコンポーネントに分けます。

1. `MustDoList`
 - `MustDoItem`をレンダーする
 - `mustdos`変数を管理する

2. `MustDoItem`
  -  `id`と`text`をレンダーする

`MustDoItem`から！

## `MustDoItem`

最初のテストを書きましょう。

```html
<script>
function assert(actual, expected) {
  if (actual != expected) {
    throw Error("やばい")
  }
}

const { shallowMount } = VueTestUtils

describe("MustDoItem", () => {
  it("idとtextをレンダーする", () => {
    const wrapper = shallowMount(MustDoItem)

    assert(wrapper.find(".id").text(), "1")
    assert(wrapper.find(".text").text(), "テストを書く")
  })
})

mocha.run()
</script>
```

そしてページをリフレッシュして：

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261560/59e9e842-c51a-c8d2-abcd-dac158d1d1f3.png)

どうなりました？

```
ReferenceError: MustDoItem is not defined
```

じゃ、作りましょう。

```html

<script>
function assert(actual, expected) {
  if (actual != expected) {
    throw Error("やばい")
  }
}

const MustDoItem = Vue.component('must-do-item', {
  template: "<div></div>"
})

const { shallowMount } = VueTestUtils

describe("TodoItem", () => {
  it("idとtextをレンダーする", () => {
    const wrapper = shallowMount(MustDoItem)

    assert(wrapper.find(".id").text(), "1")
    assert(wrapper.find(".text").text(), "テストを書く")
  })
})

mocha.run()
</script>
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261560/3203a6cd-fdb5-63a4-7536-43ac9004db72.png)

```
Error: [vue-test-utils]: find did not return .id,
cannot call text() on empty Wrapper
```

`class="id"`が必要そうです。

```js
const MustDoItem = Vue.component('must-do-item', {
  template: `
  <div>
    <div class="id"></div>
    <div class="text"></div>
  </div>`
})
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261560/2f74865f-e14a-de14-ece2-4256dcfdda56.png)

やばいまで進みました。`id`、`text`をレンダーしましょう。`MustDoList`から`props`として受け取ります。

```js

const MustDoItem = Vue.component('must-do-item', {
  props: {
    id: {
      type: Number,
      required: true
    },

    text: {
      type: String,
      required: true
    }
  },
  template: `
  <div>
    <div class="id">{{ id }}</div>
    <div class="text">{{ text }}</div>
  </div>`
})
```

まだやばいです。テストも更新しないといけません：

```js
describe("TodoItem", () => {
  it("idとtextをレンダーする", () => {
    const wrapper = shallowMount(MustDoItem, {
      propsData: {
        id: 1,
        text: "テストを書く"
      }
    })

    assert(wrapper.find(".id").text(), "1")
    assert(wrapper.find(".text").text(), "テストを書く")
  })
})
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261560/2a9c3d43-0fd4-bd28-18f1-8b560b88682f.png)

NOTやばい！！

## `MustDoList`

`MustDoList`は：

- `mustdos`配列をループして、
- `MustDoItem`をレンダーする

テストと簡単なコンポーネントを書きましょう。

```js
const MustDoList = Vue.component('must-do-list', {
  template: `
  `
})

describe("MustDoList", () => {
  it("MustdoItemを３つレンダーする", () => {
    const wrapper = shallowMount(MustDoList, {
      data() {
        return {
          mustdos: [
            { id: 1, text: "テストを書く" },
            { id: 2, text: "コードを書く" },
            { id: 3, text: "Qiitaに投稿する" },
          ]
        }
      }
    })

    assert(wrapper.findAll(".must-do").length, 3)
  })
})
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261560/f4aaa288-022f-b8a6-e0a6-a3a55a04c04d.png)

またやばい。`MustDoItem`をレンダーしましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261560/1aca64b6-bb56-53a3-d29d-217d2bbe51b9.png)

Great!

最後に、`MustDoItem`を使ってみましょう。

```html
<script>

// ...

const MustDoList = Vue.component('must-do-list', {
  data() {
    return {
      mustdos: mustdos
    }
  },

  // ...
})

// ...

const mustdos =  [
  { id: 1, text: "テストを書く" },
  { id: 2, text: "コードを書く" },
  { id: 3, text: "Qiitaに投稿する" },
]

document.addEventListener("DOMContentLoaded", () => {
  new Vue({
    el: "#app",
    template: `
      <must-do-list>
      </must-do-list>
    `
  })
})

</script>


<div style="margin-left: 50px">
  <div id="app"></div>
</div>
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261560/a42001d3-465d-1a6f-cfa1-7de6e53c623f.png)

完了です。

## まとめ

ツールは楽しいけど、たまにシンプルがいいですね。この`index.html`を使って色々なテストを書いてみてください。`vue-test-utils`のドキュメントは[こちらです](https://vue-test-utils.vuejs.org/ja/)。