うしろのこの本ください

なんでもかきます

Vueコンポーネントのmethodsだけテストする

VueはReactと違いコンポーネントからロジックを剥がそうとするモチベーションがあまり起きない作りだなーと感じている。SFCファイルに全部閉じ込めてしまった方が気持ち良い。しかしテストをmethodsやcomputedの入出力に対して行いたいものの、外出ししようとすると以下のようなthisを受け取る関数を作ることになったりする。テストはやっぱり関数に対して書きたいしなるべくvue-test-utilsに頼りたくない。

const validatePath = (path: string) => /^\/.+$/.test(path)


export default {
  data() {
    return {
      path: '/'
    }
  },
  methods: {
    validatePath() {
      return validatePath(this.path)
    },
  }
}

これはこれで良いが、Vueコンポーネントはマウントせずにmethodsやcomputedを分割代入で取り出すこともできる。テストファイルで以下のようにすれば、ロジックを関数に抽出していなくてもとりあえずテストを書くことができる。

// Login.vue
<template>
  <div>
    <input v-model="path" />
    <button @click="validatePath(path)" />
  </div>
</template>

export default {
  data() {
    return {
      path: '/'
    }
  },
  methods: {
    validatePath(path) {
      return validatePath(path)
    },
  }
}

// Login.spec.ts
import Login from '~/pages/Login'

describe('ログインページのテスト', () => {
  const { methods: { validatePath } } = Login as any

  test('validatePathメソッドが先頭スラッシュの文字列でTrueを返す', () => {
    const valid = validatePath('/Hello')
    expect(valid).toBeTruthy()
  })

  test('validatePathメソッドが先頭スラッシュじゃない文字列でFalseを返す', () => {
    const valid = validatePath('Hello/vue')
    expect(valid).toBeFalsy()
  })
})

ただし、前述のように常に関数として切り出しておいた方がテスト自体書きやすいしそもそもテストファイルにコンポーネントをimportしなくて済む。

コンポーネント設計時にテストを書くことが考慮されていなかった場合、methodsが副作用を持っていたりしてテスタブルじゃなくなっていることも多いためなるべく関数に分解することが大切。特にVueはthisというコンテキストに依存するライブラリなのでこれがじわじわ効いてくる。

殆どの問題はVue3で解消するが、今のうちにテスタブルな関数の勘所を抑えておくと違和感なく移行できると思う。