0
点赞
收藏
分享

微信扫一扫

【架构师(第二十九篇)】Vue-Test-Utils 触发事件和异步请求

知识点

  • 将​​mock​​​ 对象断言为特定类型 使用​​jest.Mocked<T> ​
  • 使用​​it.only​​​ 来指定测试的​​case​
  • 使用​​skip​​​ 跳过指定测试的​​case​

测试内容

  • 触发事件
  • ​trigger​​ 方法
  • 测试界面是否更新
  • 特别注意​​DOM​​ 更新是个异步的过程
  • 使用​​async await​
  • 更新表单
  • ​setValue​​ 方法
  • 验证事件是否发送
  • ​emitted​​ 方法
  • 测试异步请求
  • 模拟第三方库实现

测试准备和结束

可以使用内置的一些钩子来简化一些通用的逻辑,以下钩子用于一次性完成测试准备。

  • beforeAll
  • afterAll
let wrapper: VueWrapper<any>;
describe('HelloWorld.vue', () => {
// 在多个 case 运行之前执行,只执行一次,由于这样会让所有的用例使用一个 `warpper` 实例,可能会造成错误。
beforeAll(() => {
// 获取组件
wrapper = shallowMount(HelloWorld, {
props: { msg },
});
});
// 在多个 case 运行之后执行,只执行一次
afterAll(() => {});
});

以下钩子用于每个测试用例测试准备。

  • beforeEach
  • afterEach
let wrapper: VueWrapper<any>;
describe('HelloWorld.vue', () => {
beforeEach(() => {
// 获取组件
wrapper = shallowMount(HelloWorld, {
props: { msg },
});
});
afterEach(() => {
mockAxios.get.mockReset();
});
});

测试建议

如果一个测试失败了,需要注意

  • 它是否是唯一在运行的用例,使用​​only​​ 单独运行一次
  • 如果单独运行没问题,整体运行出错,应该检查​​beforeEach​​​ ,​​beforeAll​​ 等全局钩子中的逻辑是否有问题,判断是否需要清空共享状态。

测试组件

父组件

<template>
<div>
<!-- 显示 props.msg 内容 -->
<h1>{{ msg }}</h1>
<!-- 按钮 点击 count ++ -->
<button class="add-count"
@click="addCount">{{ count }}</button>
<!-- 输入框 -->
<input type="text"
v-model="todo" />
<!-- 按钮 点击添加 todo -->
<button class="add-todo"
@click="addTodo">addTodo</button>
<!-- . todos 列表 渲染 todo -->
<ul>
<li v-for="(todo, index) in todos"
:key="index">{{ todo }}</li>
</ul>
<!-- 按钮 点击发起异步请求 -->
<button class="load-user"
@click="loadUser">loadUser</button>
<!-- 加载动画 -->
<p v-if="user.loading"
class="loading">loading</p>
<!-- 显示数据 -->
<div v-else
class="user-name">{{ user?.data?.username }}</div>
<!-- 错误提示 -->
<p v-if="user.error"
class="error">error</p>
<!-- 子组件 传递 msg = 1234 -->
<hello-com msg="1234"></hello-com>
</div>
</template>

<script setup lang="ts">
import axios from 'axios'
import { defineProps, ref, defineEmits, reactive } from 'vue'
import HelloCom from './hello.vue'

// 定义props
defineProps({
msg: String
})

// 定义事件
const emit = defineEmits(['send'])

// 初始化 count
const count = ref(0)

// count++
const addCount = () => {
count.value++
}

// 初始化 input 内容
const todo = ref('')

// 初始化 todos 列表
const todos = ref<string[]>([])

// 添加 todo 到 todos 列表
const addTodo = () => {
if (todo.value) {
todos.value.push(todo.value)
emit('send', todo.value)
}
}

// 初始化异步请求数据
const user = reactive({
data: null as any,
loading: false,
error: false
})

// 异步请求
const loadUser = () => {
user.loading = true
axios.get("https://jsonplaceholder.typicode.com/users/1").then((resp) => {
console.log(resp)
user.data = resp.data
}).catch(() => {
user.error = true
}).finally(() => {
user.loading = false
})
}
</script>

子组件

<template>
<h1>{{ msg }}</h1>
</template>

<script setup lang="ts">
import { defineProps } from 'vue'
const props = defineProps({
msg: String
})
</script>

测试代码

​Dom​​​ 更新为异步操作,需要使用 ​​async await​​。

import axios from 'axios';
import flushPromises from 'flush-promises';
import type { VueWrapper } from '@vue/test-utils';
import { shallowMount } from '@vue/test-utils';
import HelloWorld from '@/components/HelloWorld.vue';

jest.mock('axios');
//将 mock 对象断言为特定类型 使用 jest.Mocked<T>
const mockAxios = axios as jest.Mocked<typeof axios>;
const msg = 'new message';
let wrapper: VueWrapper<any>;
describe('HelloWorld.vue', () => {
beforeEach(() => {
// 获取组件
wrapper = shallowMount(HelloWorld, {
props: { msg },
});
});
afterEach(() => {
mockAxios.get.mockReset();
});
// 测试点击 button, count 增加
it('should update the count when clicking the button', async () => {
// 触发点击事件
await wrapper.get('.add-count').trigger('click');
// 数字变为 1 (初始为0)
expect(wrapper.get('.add-count').text()).toBe('1');
});
// 测试 更新表单 点击 add button
it('should add todo when fill the input and click the add button', async () => {
const todoContent = 'test todo';
// 触发 input 事件 , 设置值为 todoContent
await wrapper.get('input').setValue(todoContent);
// 断言 input 的值为 todoContent
expect(wrapper.get('input').element.value).toBe(todoContent);
// 触发 button 点击事件
await wrapper.get('.add-todo').trigger('click');
// 断言 有一个 li
expect(wrapper.findAll('li')).toHaveLength(1);
// 断言 li 的内容是 todoContent
expect(wrapper.get('li').text()).toBe(todoContent);
// 断言 触发了 名为 send 的 emit 事件
expect(wrapper.emitted()).toHaveProperty('send');
// 获取 send 事件的 对象
const events = wrapper.emitted('send')!;
// 检查对象内容是否相同使用 toEqual, toBe 要求引用也相同
expect(events[0]).toEqual([todoContent]);
});
// 使用 it.only 来指定测试的 case
it('should load user message when click the load button', async () => {
// mock service
mockAxios.get.mockResolvedValueOnce({
data: {
username: 'warbler',
},
});
// 触发点击事件
await wrapper.get('.load-user').trigger('click');
// 断言 请求是否被调用
expect(mockAxios.get).toHaveBeenCalled();
// 断言 loading 是否出现
expect(wrapper.find('.loading').exists()).toBeTruthy();
// 让 promise 完成,并且界面更新完成
await flushPromises();
// 断言 loading 消失
expect(wrapper.find('.loading').exists()).toBeFalsy();
// 断言 username 显示
expect(wrapper.get('.user-name').text()).toBe('warbler');
});
});

测试结果

【架构师(第二十九篇)】Vue-Test-Utils 触发事件和异步请求_ios

举报

相关推荐

0 条评论