11
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.js #3Advent Calendar 2017

Day 5

Vuexの状態を包括的にテストする

Last updated at Posted at 2017-12-04

やること

  • vue + vuexで入力フォームを構築する
  • 入力、バリデーションなどの状態をすべてvuexに持たせる
  • 正しい入力、誤った入力などの状態を起こしてテストする

ポイント

関数単位のテストではなく、連続的に状態を変更させてテストする

使用環境

  • vue-cliのwebpackテンプレート
  • vue + vuex
  • 見やすくするためBootstrapを使用
  • デフォルトのテスト環境を使用する
  • karma + mocha + chai

Demo

https://renowan.github.io/qiita-vuex-test/

フォーム仕様

  • 入力項目name(名前), age(年齢)
  • 両方とも入力必須
  • 名前は4文字以上
  • 年齢は数字のみ、かつ0以上
  • 名前、年齢入力済み、エラーなしの場合、Submitが押せるようになる(disabled = false)
  • Clearを押したらデータが初期化される

Storeの構造

// actions
const actions = {
  // 入力
  input ({ commit, state }, data) {
    let value = data.value
    // name
    if (data.key === 'name') {
      commit(types.INPUT_NAME, value)
    }
    // age
    if (data.key === 'age') {
      // 数字なら数字に変換する
      if (Number.isInteger(Number(value))) {
        value = Number(value)
      }
      commit(types.INPUT_AGE, value)
    }
  }
}

// mutations
const mutations = {
  [types.INIT] (state) {
    state.name = ''
    state.age = null
    state.disabled = true
  },
  [types.INPUT_NAME] (state, data) {
    state.name = data
  },
  [types.INPUT_AGE] (state, data) {
    state.age = data
  }
}

// getters
const getters = {
  app: state => state,
  // nameが未入力、または4文字未満
  isNameError: state => {
    const name = state.name
    return name === '' || name.length < 4
  },
  // ageは数字以外、または1未満
  isAgeError: state => {
    const age = state.age
    return !(Number.isInteger(age) && age > 0)
  },
  // name と ageどっちがエラー
  disabled: (state, getters) => {
    return getters.isNameError || getters.isAgeError
  }
}

テスト開始

まずはstoreのimport

import store from '@/store/'

必要なものを宣言しておく

const app = store.state.app
const dispatch = store.dispatch
const commit = store.commit
const getters = store.getters

初期状態の確認

describe('初期状態テスト', () => {
  it('stateの初期状態', () => {
    expect(app.name).to.equal('')
    expect(app.age).to.equal(null)
  })
  it('gettersの初期状態', () => {
    expect(getters.isNameError).to.equal(true)
    expect(getters.isAgeError).to.equal(true)
    expect(getters.disabled).to.equal(true)
  })
})

入力してみよう

describe('入力テスト', () => {
  it('nameを入力', () => {
    dispatch('input', {key: 'name', value: 'myname'})
    expect(app.name).to.equal('myname')
  })
  it('ageを入力', () => {
    dispatch('input', {key: 'age', value: 3})
    expect(app.age).to.equal(3)
  })
})

バリデーションチェック

正しく入力したのでエラーはない。submitも押せるはず

it('バリデーションテストエラーが起きない、submitボタンが押せる', () => {
  expect(getters.isNameError).to.equal(false)
  expect(getters.isAgeError).to.equal(false)
  expect(getters.disabled).to.equal(false)
})

ちゃんとエラーチェックできているか?

it('ageを0と入力してageエラーを起こす', () => {
  dispatch('input', {key: 'age', value: 0})
  expect(getters.isAgeError).to.equal(true)
})
it('nameをabcと入力して文字数が足りず、エラーになる', () => {
  dispatch('input', {key: 'name', value: 'abc'})
  expect(getters.isNameError).to.equal(true)
})

入力を修正して再度エラーなしに戻る

it('nameとageをエラーが起きないように入力する、ボタンが押せるようになる', () => {
  dispatch('input', {key: 'name', value: 'abcdef'})
  dispatch('input', {key: 'age', value: 4})
  expect(getters.isNameError).to.equal(false)
  expect(getters.isAgeError).to.equal(false)
  expect(getters.disabled).to.equal(false)
})

Clearボタンを押せて初期化する

it('Clearを押して初期化する->またエラーになる', () => {
  commit('INIT')
  expect(getters.isNameError).to.equal(true)
  expect(getters.isAgeError).to.equal(true)
  expect(getters.disabled).to.equal(true)
})

値がすべてデフォルトに戻されたことを確認できた

完成

いかがでしょうか? このように状態をすべてstoreに持たせて、いろんなシチュエーションを起こしてテストをかければアプリの動作はしっかり担保できる。ロジックはすべてstoreにあるので、表示用のコンポーネントのテストは最低限に抑えられて効率がよくなると思う。

リポジトリ

11
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?