やること
- 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にあるので、表示用のコンポーネントのテストは最低限に抑えられて効率がよくなると思う。
リポジトリ