のぐそんブログ

暗いおじさんがシコシコ書くブログです。

Vueでunitテストをする為の基礎基礎メモ

準備

  • vue-cliでプロジェクトを作成する
  • Jestを利用する。
  • vue-test-utilsを利用する(vue-cliでプロジェクトを作成した場合はインストール済み)

Jestとは

Facebook製のテスト用フレームワーク

vue-test-utilsとは

公式より

vue-test-utilsは Vue.js 向けの公式単体テストライブラリです。

テストを書く

プロジェクト作成時は、以下にtest用のファイルがあります。 こちらを参考にするとわかりやすいかもしれません。

/tests/unit/example.spec.js

describe

テストの概要を記載します。 テストをまとめる単位としても利用します。

it

テストケースです。 テストの目的を記載します。

describe('テストの概要', () => {
  it('テストケース1', () => {
  })
})

テストを実行

以下のCommandでテストを実行できます。

npm run test:unit

実行結果

テストがうまく動くと下記のような表示になります。

 PASS  tests/unit/example.spec.js
  テストの概要
    ✓ テストケース1 (1ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.285s

テストを書く (その2)

上記ではテスト内容がないものを実行していましたが、今度はテスト内容を追加していきます。

Vueコンポーネントをマウントする

Vueコンポーネントをテストするには、まずVueコンポーネントをマウントする必要があります。 マウントするには、mountshallowMountのいずれかを利用します。

mountshallowMountはvue-test-utilsの関数なので、利用するにはライブラリをインポートする必要があります。

import { shallowMount } from '@vue/test-utils'
関数名 説明 利用ケース
mount コンポーネントに実際のコンポーネントを使う コンポーネント同士で連携が必要な場合
shallowMount コンポーネントにダミーのコンポーネントを使う コンポーネント単体でのテスト

※mountを利用した場合は、テスト対象のコンポーネントの子コンポーネントの変更もテストに影響を受けます。

以下でコンポーネントをマウントします。 戻り値として、コンポーネントラッパーを返却します。 このラッパーを利用してテストを行います。

const wrapper = shallowMount(HelloWorld)

Assertionを作成

コンポーネントが期待する動作をするかを判定するにはAssertionを作成する必要があり、その為にexpectAPIを利用します。

expect(result).to [matcher] (actual)

new messageの文字列が画面内にあるかをテスト。

HelloWorld.spec.js
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'

describe('HelloWorld.vue', () => {
  it('テストケース1', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    // HelloWorldコンポーネント内のテキストを正規表現でチェック(.toMatch(regexp))
    expect(wrapper.text()).toMatch(msg)
  })
})
HelloWorld.vue
<template>
    <h1>{{ msg }}</h1>
</template>
<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

テストマッチャー

テストで様々な値をテストとする為にMatcherを利用します。 Matcherは以下の形式で利用します。

expect(result).to [matcher] (actual)
Matcher 説明 使用例
.toBe(value) resultとactual等価であることの比較 expect(4).toBe(4)
.toEqual(value) resultとactualの連想配列の中身が一致するかの比較 expect({two: 2,one: 1}).toEqual({one: 1,two: 2})
.toMatch(regexp) resultに対して正規表現で比較 expect('abcdefg').toMatch(/abc/)
.toBeGreaterThan(number) resultがactualより大きい expect(11).toBeGreaterThan(10)
.toBeGreaterThanOrEqual(number) resultがactual以上 expect(10).toBeGreaterThanOrEqual(10)
.toBeLessThan(number) resultがactualより小さい expect(9).toBeLessThan(10)
.toBeLessThanOrEqual(number) resultがactual以下 expect(10).toBeLessThanOrEqual(10)
.toBeCloseTo(number,桁数) resultの特定の少数桁までactualと一致するかを比較(jsは小数点計算で誤差がでる為) expect(0.1 + 0.2).toBeCloseTo(0.35, 1)
.toHaveLength(number) resultのlengthとactualと一致するかを比較 expect([1,2]).toHaveLength(2)
.toBeInstanceOf(class) resultがactualと同じclassのインスタンス化か比較 class Klass {}
expect(new Klass()).toBeInstanceOf(Klass)
.toHaveProperty(value) resultの特定のkeyと一致するか expect({a:false,b:{c:1,d:'A'}}).toHaveProperty('a') // key aがあるか
expect({a:false,b:{c:1,d:'A'}}).toHaveProperty('a',false) // key aはfalseか
expect({a:false,b:{c:1,d:'A'}}).toHaveProperty('b.c',1)
.toMatchObject(value) resultの一部とactualが一致するか expect({a:1,b:2}).toMatchObject({a: 1})
.toContain(value) resultにactualが入っているか expect([1,2]).toContain(2)
expect('ABCDEFG').toContain('EFG')
.toBeNull() resultがNullかどうか expect(null).toBeNull()
.toBeUndefined() resultがUndefinedかどうか expect().toBeUndefined()

.notを利用して否定で判定することもできます。

expect(2).not.toBe(1)

Matcherは他にもたくさんあります。 こちらを確認してください。

vue-test-utilsの使い方

vue-test-utilsはVue.jsの公式単体テスト用ライブラリです。 vue-cliを利用した場合は、プロジェクトに含まれています。

propsDataの使い方

mountshallowMountで利用することができる。 propsDataは親コンポーネントから、propsとして渡されたものとして利用することができる。

HelloWorld.vue
<template>
  <div>
    <span v-if="flag === true">{{msg}}</span>
  </div>
</template>

  <script>
  export default {
    name: 'HelloWorld',
    props: {
      msg: String,
      flag:Boolean
    }
  }
  </script>
HelloWorld.spec.js
ERROR

new messageの文言が画面内に無いので、これはエラーになります。

import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'

describe('HelloWorld.vue', () => {
  it('テストケース1', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    expect(wrapper.text()).toMatch(msg)
  })
})
OK

new messageの文言が画面内に無いので、これは成功します。

import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'

describe('HelloWorld.vue', () => {
  it('テストケース1', () => {
    const msg = 'new message'
    const flag = true
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg ,flag}
    })
    expect(wrapper.text()).toMatch(msg)
  })
})

使いそうなWrapperメソッド

HelloWorld.vueはこちらとします。

<template>
  <div>
    <span>{{msg}}</span>
    <span @click="changeMsg">{{clickMsg}}</span>
  </div>
</template>

  <script>
  export default {
    name: 'HelloWorld',
    props: {
      msg: String
    },
    data(){
      return{
        clickMsg :''
      }
    },
    methods:{
      changeMsg(arg){
        this.clickMsg = `${arg.hogehoge}回クリックしたよ`
      }
    }
  }
  </script>

props()

Vueコンポーネントインスタンスのpropsを返却します。

describe('HelloWorld.vue', () => {
  it('テストケース', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    expect(wrapper.props().msg).toBe('new message')
    
  })
})

setProps(props)

Vueコンポーネントインスタンスにpropsを設定します。
すでにマウントされているコンポーネントのプロパティを更新することができます。

describe('HelloWorld.vue', () => {
  it('テストケース', () => {
    const wrapper = shallowMount(HelloWorld)
    wrapper.setProps({ flag: true })
    expect(wrapper.vm.flag).toBe(true)
  })
})

ただし、mount時にsync:falseを設定ししてると反映されません。

find(selector)、findAll(selector)

find()と findAll()は、インスタンスのDOMの中から、selectorと一致する DOMまたはVueコンポーネントを取得するのに利用します。

describe('HelloWorld.vue', () => {
  it('テストケース', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    const elm = wrapper.find('span')  
    expect(elm.is('span')).toBe(true)
  })
})

trigger(eventType [, options ])

DOMのイベントを発火することができます。

describe('HelloWorld.vue', () => {
  it('テストケース', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    const elm = wrapper.findAll('span').at(1)

    elm.trigger('click')

    expect(wrapper.text()).toMatch('クリックしたよ')
  })
})

以下のような感じで引数を渡すこともできます。

describe('HelloWorld.vue', () => {
  it('テストケース', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    const elm = wrapper.findAll('span').at(1)

    elm.trigger('click',{hogehoge:2})

    expect(wrapper.text()).toMatch('2回クリックしたよ')
  })
})

他にもたくさんあります。 こちらを参照してください。

まとめ

ほん〜〜〜の一部だけまとめてみました。 テスト難しいです。