ログインフォームがあります。ログインフォームにデータを入力してログインボタンをクリックすると、次のようになります。
<flash-message>
コンポーネントによって表示されますこのコンポーネントはVuexストアに大きく依存しているため、このコンポーネントのいくつかの有効なテストケースを考えることができません。
JavaScriptエコシステムの経験がないので、詳細に説明していただければ幸いです。
Login.vue
<template>
<b-col sm="6" offset-sm="3">
<h1><span class="fa fa-sign-in"></span> Login</h1>
<flash-message></flash-message>
<!-- LOGIN FORM -->
<div class="form">
<b-form-group>
<label>Email</label>
<input type="text" class="form-control" name="email" v-model="email">
</b-form-group>
<b-form-group>
<label>Password</label>
<input type="password" class="form-control" name="password" v-model="password">
</b-form-group>
<b-btn type="submit" variant="warning" size="lg" @click="login">Login</b-btn>
</div>
<hr>
<p>Need an account? <b-link :to="{name:'signup'}">Signup</b-link></p>
<p>Or go <b-link :to="{name:'home'}">home</b-link>.</p>
</b-col>
</template>
<script>
export default {
data () {
return {
email: '',
password: ''
}
},
methods: {
async login () {
this.$store.dispatch('login', {data: {email: this.email, password: this.password}, $router: this.$router})
}
}
}
</script>
Vue test utilsのドキュメント 言います:
[W] eは、コンポーネントのパブリックインターフェイスをアサートし、その内部をブラックボックスとして扱うテストを作成することをお勧めします。単一のテストケースは、コンポーネントに提供された入力(ユーザーの操作または小道具の変更)が期待される出力(レンダリング結果または出力されたカスタムイベント)をもたらすことを表明します。
したがって、bootstrap-vueコンポーネントをテストするべきではありません。それは、そのプロジェクトのメンテナの仕事です。
コンポーネントのテストを容易にするために、コンポーネントをそれぞれの責任でスコープすることが役立ちます。つまり、ログインフォームは独自のSFC(単一ファイルコンポーネント)である必要があり、ログインページはログインフォームを使用する別のSFCです。
ここでは、ログインページから分離されたログインフォームがあります。
<template>
<div class="form">
<b-form-group>
<label>Email</label>
<input type="text" class="form-control"
name="email" v-model="email">
</b-form-group>
<b-form-group>
<label>Password</label>
<input type="password" class="form-control"
name="password" v-model="password">
</b-form-group>
<b-btn type="submit" variant="warning"
size="lg" @click="login">
Login
</b-btn>
</div>
</template>
<script>
export default {
data() {
return { email: '', password: '' };
},
methods: {
login() {
this.$store.dispatch('login', {
email: this.email,
password: this.password
}).then(() => { /* success */ }, () => { /* failure */ });
}
}
}
</script>
ログインが成功または失敗したときにリダイレクトを処理するのはストアの責任ではないため、ストアアクションディスパッチからルーターを削除しました。ストアは、その前にフロントエンドがあることを知る必要はありません。データとデータに関連する非同期リクエストを処理します。
ストアアクションを個別にテストします。次に、コンポーネントで完全にモックすることができます。
ここでは、ストアが本来の目的を果たしていることを確認したいと思います。したがって、状態に正しいデータがあること、それらをモックしながらHTTP呼び出しが行われることを確認できます。
import Vuex from 'vuex';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import storeConfig from '@/store/config';
describe('actions', () => {
let http;
let store;
beforeAll(() => {
http = new MockAdapter(axios);
store = new Vuex.Store(storeConfig());
});
afterEach(() => {
http.reset();
});
afterAll(() => {
http.restore();
});
it('calls login and sets the flash messages', () => {
const fakeData = { /* ... */ };
http.onPost('api/login').reply(200, { data: fakeData });
return store.dispatch('login')
.then(() => expect(store.state.messages).toHaveLength(1));
});
// etc.
});
このコンポーネントが行う唯一の実際のことは、送信ボタンが呼び出されたときにlogin
アクションをディスパッチすることです。したがって、これをテストする必要があります。アクション自体はすでに個別にテストされているため、テストする必要はありません。
import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import LoginForm from '@/components/LoginForm';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Login form', () => {
it('calls the login action correctly', () => {
const loginMock = jest.fn(() => Promise.resolve());
const store = new Vuex.Store({
actions: {
// mock function
login: loginMock
}
});
const wrapper = mount(LoginForm, { localVue, store });
wrapper.find('button').trigger('click');
expect(loginMock).toHaveBeenCalled();
});
});
同じように、挿入されたメッセージでストアの状態をモックし、各メッセージアイテム、クラスなどの存在をテストして、FlashMessage
コンポーネントがメッセージを正しく表示することを確認する必要があります。
ログインページコンポーネントを単なるコンテナにすることができるようになったため、テストする必要はほとんどありません。
<template>
<b-col sm="6" offset-sm="3">
<h1><span class="fa fa-sign-in"></span> Login</h1>
<flash-message />
<!-- LOGIN FORM -->
<login-form />
<hr>
<login-nav />
</b-col>
</template>
<script>
import FlashMessage from '@/components/FlashMessage';
import LoginForm from '@/components/LoginForm';
import LoginNav from '@/components/LoginNav';
export default {
components: {
FlashMessage,
LoginForm,
LoginNav,
}
}
</script>
mount
とshallow
をいつ使用するかshallow
に関するドキュメントは次のように述べています。
mount
のように、マウントおよびレンダリングされたVueコンポーネントを含むWrapper
を作成しますが、スタブされた子コンポーネントを使用します。
つまり、コンテナコンポーネントの子コンポーネントは<!-- -->
コメントに置き換えられ、それらのすべての対話性はそこにありません。そのため、テスト対象のコンポーネントを、その子が持つ可能性のあるすべての必要なものから分離します。
ログインページに挿入されたDOMはほとんど空になり、FlashMessage
、LoginForm
、およびLoginNav
コンポーネントが置き換えられます。
<b-col sm="6" offset-sm="3">
<h1><span class="fa fa-sign-in"></span> Login</h1>
<!-- -->
<!-- LOGIN FORM -->
<!-- -->
<hr>
<!-- -->
</b-col>