Vue Test Utilsを使ってみた

Vue.jsのUIテストをやってみた備忘録

## 文字列が正しく表示されているか
```vue:TextTest.vue
<template>
  <h1 id="main-title">タイトルですタイトルです</h1>
  <h2 id="sub-title">サブタイトルですサブタイトルです</h2>
</template>
```

```javascript:text.spec.js
describe('タイトル部分のテスト', () => {
  const wrapper = mount(TextTest)
  // find関数でテストしたい部分を指定することができる
  const mainTitle = wrapper.find('#main-title')
  const subTitle = wrapper.find('#sub-title')
  it('メインタイトルの要素が存在すること', () => {
    expect(mainTitle.exists()).toBeTruthy()
  })

  it('メインタイトル文字列が表示されていること', () => {
    expect(mainTitle.text()).toBe('タイトルですタイトルです')
  })

  it('サブタイトルの要素が存在すること', () => {
    expect(subTitle.exists()).toBeTruthy()
  })

  it('サブタイトルの文字列が表示されていること', () => {
    expect(subTitle.text()).toBe('サブタイトルですサブタイトルです')
  })
})
```

## 描画されたHTMLの構造が正しいかどうか
```vue:HtmlTest.vue
<template>
  <span>span要素</span>
</template>
```

```javascript:html.spec.js
describe('html構造のテスト', () => {
  it('span要素があるか', () => {
    const wrapper = mount(HtmlTest)
    expect(wrapper.html()).toContain('<span>span要素</span>')
  })
})
```

## ボタンイベント
```vue:ClickTest.vue
<template>
  <div>
    <span>{{ count }}</span>
    <button @click="increment()" id="increment-btn">増加</button>
    <button @click="decrement()" id="decrement-btn">減少</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    increment() {
      // 10より大きくなった時は10にする
      if (this.count >= 10) {
        return;
      }
      this.count++;
    },
    decrement() {
      // 0より小さくなった時は0にする
      if (this.count <= 0) {
        return;
      }
      this.count--;
    },
  },
};
</script>
```

```javascript:click.spec.js
describe('clickイベントのテスト', () => {
  const wrapper = mount(ClickTest)
  it('カウンターの初期値は0である', () => {
    expect(wrapper.vm.count).toBe(0)
  })

  it('増加ボタンが存在すること', () => {
    expect(wrapper.find('#increment-btn').exists()).toBeTruthy()
  })

  it('減少ボタンが存在すること', () => {
    expect(wrapper.find('#decrement-btn').exists()).toBeTruthy()
  })

  it('増加ボタンを押すと count が1増えること', () => {
    const btn = wrapper.find('#increment-btn')
    btn.trigger('click')
    expect(wrapper.vm.count).toBe(1)
  })

  it('減少ボタンを押すと count が1減ること', () => {
    const btn = wrapper.find('#decrement-btn')
    btn.trigger('click')
    expect(wrapper.vm.count).toBe(0)
  })

  it('count が0であること', () => {
    expect(wrapper.vm.count).toBe(0)
  })

  it('減少ボタンを押した時に0よりも小さくならないこと', () => {
    const btn = wrapper.find('#decrement-btn')
    btn.trigger('click')
    expect(wrapper.vm.count).toBe(0)
  })

  it('増加ボタンを10回押すと count が10になること', () => {
    const btn = wrapper.find('#increment-btn')
    for (var i = 0; i < 10; i++) {
      btn.trigger('click')
    }
    expect(wrapper.vm.count).toBe(10)
  })

  it('増加ボタンを押しても10より大きい値にはならないこと', () => {
    const btn = wrapper.find('#increment-btn')
    btn.trigger('click')
    expect(wrapper.vm.count).toBe(10)
  })
})
```

## 表示非表示
```vue:VisibleTest.vue
<template>
  <span v-if="isVisible" id="span-text">span要素です</span>
  <button @click="changeVisible" id="btn">表示</button>
</template>

<script>
export default {
  data() {
    return {
      isVisible: false,
    };
  },
  methods: {
    changeVisible() {
      this.isVisible = !this.isVisible;
    },
  },
};
</script>
```

```javascript:visible.spec.js
import { mount } from "@vue/test-utils";
import { nextTick } from 'vue'
import VisibleTest from '@/components/VisibleTest.vue';

describe('表示非表示のテスト', () => {
  const wrapper = mount(VisibleTest)
  const btn = wrapper.find('#btn')
  it('button要素が存在すること', () => {
    expect(btn.exists()).toBeTruthy()
  })
  it('button要素を押すとspna要素が現れること', async () => {
    btn.trigger('click')
    // domの更新を待つ
    await nextTick()
    const spanElement = wrapper.find('#span-text')
    expect(spanElement.exists()).toBeTruthy()
  })
  it('もう一度ボタンを押すとspan要素がなくなること', async () => {
    btn.trigger('click')
    // domの更新を待つ
    await nextTick()
    const spanElement = wrapper.find('#span-text')
    expect(spanElement.exists()).toBeFalsy()
  })
})
```

## propsのテスト
```vue:PropsTest.vue
<template>
  <span id="props-text">{{ msg }}</span>
</template>

<script>
export default {
  props: {
    msg: {
      type: String,
      required: true,
    },
  },
};
</script>
```

```javascript:props.spec.js
import { mount } from "@vue/test-utils";
import PropsTest from '@/components/PropsTest.vue';

describe('propsのテスト', () => {
  const wrapper = mount(PropsTest, {
    props: {
      msg: 'Hello World'
    }
  })
  it('propsにわたした文字列が正しく表示されていること', () => {
    expect(wrapper.find('#props-text').text()).toBe('Hello World')
  })
})
```

## 参考文献
[Vue Test Utils公式](https://v1.test-utils.vuejs.org/ja/)