Vue test utils documentation说:
[W] e建议编写断言组件公共接口的测试,并将其内部视为黑盒.单个测试用例将断言,提供给组件的一些输入(用户交互或props 的更改)会导致预期的输出(呈现结果或发出的自定义事件).
所以我们不应该测试 bootstrap vue组件,这是该项目维护人员的工作.
Write code with unit tests in mind
为了使测试组件更容易,将组件的范围限定在他们自己的责任范围内将有所帮助.这意味着登录表单应该是它自己的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>
我从store action dispatch中删除了路由,因为当登录成功或失败时,处理重定向不是store的责任.store 不必知道前面有一个前端.它处理数据和与数据相关的异步请求.
Test each part independently
单独测试存储操作.然后可以在组件中完全模拟它们.
测试store 行为
在这里,我们想确保store 做它该做的事.因此,我们可以判断该州是否有正确的数据,在模拟HTTP调用时是否进行了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.
});
测试我们的简单登录表单
该组件唯一真正做的事情是在调用submit按钮时发送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();
});
});
测试flash消息组件
同样,我们应该用注入的消息模拟存储状态,并通过测试每个消息项、类等的存在,确保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>
When to use mount
vs shallow
documentation on shallow
人说:
与mount
类似,它创建了一个Wrapper
,其中包含已装载和渲染的Vue组件,但带有存根子组件.
这意味着容器组件中的子组件将被替换为<!-- -->
条注释,它们的所有交互性都将不存在.因此,它将被测试的组件与其子组件可能具有的所有要求隔离开来.
然后,DOM将被替换为FlashMessage
个登录组件中的FlashMessage
个:
<b-col sm="6" offset-sm="3">
<h1><span class="fa fa-sign-in"></span> Login</h1>
<!-- -->
<!-- LOGIN FORM -->
<!-- -->
<hr>
<!-- -->
</b-col>