問題
Vue.jsのコンポーネントのユニットテストをTypeScript+Jest+vue-test-utilsで書いていた。
しかし、テストコードから、コンポーネントで定義したはずのメソッドが使用できなかった。
環境
Vue.js 2.6.11
TypeScript 4.1.5
jest 24.9.0
@vue/test-utils 1.0.3
該当のソースコード
Login.vue
<template>
<div class="login">
<h2>ログイン</h2>
<ValidationObserver ref="observer" v-slot="{ invalid }">
<v-form ref="form" lazy-validation>
<!-- errors -->
<div class="errors" v-if="errors.length !== 0">
<ul>
<li v-for="e in errors" :key="e"><v-alert type="error" dense text>{{ e }}</v-alert></li>
</ul>
</div>
<ValidationProvider v-slot="{ errors }" name="メールアドレス" rules="required|email|max:250">
<v-text-field
v-model="user.email"
id="email"
:counter="250"
label="メールアドレス"
:error-messages="errors"
required
></v-text-field>
</ValidationProvider>
<ValidationProvider v-slot="{ errors }" name="パスワード" rules="required|min:6">
<v-text-field
v-model="user.password"
type="password"
id="password"
label="パスワード"
:error-messages="errors"
required
></v-text-field>
</ValidationProvider>
<v-btn color="primary" class="login" @click="login" :disabled="invalid" data-cy-login-button>ログイン</v-btn>
</v-form>
</ValidationObserver>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { required, min, email, max } from 'vee-validate/dist/rules';
import { extend, ValidationObserver, ValidationProvider, setInteractionMode } from 'vee-validate';
import User from '@/modules/users'
setInteractionMode('eager');
extend('min', {
...min,
message: '{_field_}は{length}文字以上で入力してください',
});
extend('required', {
...required,
message: '{_field_}を入力してください',
});
extend('max', {
...max,
message: '{_field_}は{length}文字以内で入力してください',
});
extend('email', {
...email,
message: '{_field_}は正しい形式で入力してください',
});
type user = {
email: string,
password: string
};
@Component({
components: {
ValidationObserver,
ValidationProvider
}
})
export default class Login extends Vue {
private user: user = { email: '', password: '' };
private errors: string[] = [];
public login(): void {
User.login(this.user, this);
}
}
</script>
<style scoped>
h2 {
text-align: center;
}
.login {
max-width: 500px;
text-align: center;
margin: 0 auto;
margin-top: 30px;
padding-left: 10px;
padding-right: 10px;
}
</style>
login.spec.ts
import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
import Login from '@/views/auth/Login.vue';
import nextTick from 'vue';
import Vuex from 'vuex';
import store from '@/store/index';
import VueRouter from 'vue-router';
import router from '@/router/index';
import axios from 'axios';
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(VueRouter);
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
describe('Login.vue', () => {
describe('Data', () => {
it('sets user', () => {
const wrapper = shallowMount(Login, { localVue });
expect(wrapper.vm.$data.user.email).toBe('');
expect(wrapper.vm.$data.user.password).toBe('');
})
it('sets errors', () => {
const wrapper = shallowMount(Login, { localVue });
expect(wrapper.vm.$data.errors).toEqual([]);
})
})
describe('Methods', () => {
describe('login', () => {
const mockUser = { id: 1, name: 'foo', email: 'foo@bar.com' };
const resp_200 = { data: { data: mockUser }, status: 200 };
describe('when status is 200', () => {
it('sets flash', async () => {
mockedAxios.post.mockResolvedValueOnce(resp_200);
const wrapper = shallowMount(Login, { store, router, localVue });
await wrapper.vm.login();
expect(wrapper.vm.$store.state.flash).toEqual({ msg: 'ログインしました', type: 'success' });
})
})
})
})
})
解決方法
Wrapperインターフェイスをインポートする。
Login.vue
import { Wrapper } from '@vue/test-utils'
次に、wrapperオブジェクトを作成している箇所を以下のように直す。
Login.vue
const wrapper: Wrapper<Login & { [key: string]: any}> = shallowMount(Login, { store, router, localVue });
参考記事