当前位置: 首页 > article >正文

Jest进阶:测试 Vue 组件

在 Vue 项目中,测试组件是确保应用质量和稳定性的关键步骤。Vue Test Utils 是一个专门为 Vue.js 应用程序编写的单元测试和集成测试工具库。它提供了丰富的 API,帮助开发者模拟用户操作、查询组件和断言测试结果,从而在不需要手动操作应用程序的情况下自动化地测试 Vue 组件的行为和交互。

安装 Vue Test Utils

首先,确保你已经安装了 Vue Test Utils。根据你使用的 Vue 版本,安装相应的 Vue Test Utils 版本:

  • Vue 2:

    npm install @vue/test-utils@1 --save-dev
    
  • Vue 3:

    npm install @vue/test-utils@next --save-dev
    
快速上手

假设我们有一个简单的 Vue 组件 HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

接下来,我们编写测试代码:

import { shallowMount } from '@vue/test-utils';
import HelloWorld from '@/components/HelloWorld.vue';

describe('HelloWorld.vue', () => {
  it('renders props.msg when passed', () => {
    const msg = 'new message';
    const wrapper = shallowMount(HelloWorld, {
      props: { msg }
    });
    expect(wrapper.text()).toMatch(msg);
  });
});

在这个测试中,我们使用 shallowMount 渲染 HelloWorld 组件,并传递 msg 属性。然后,我们使用 expect 断言组件的文本内容是否包含传递的 msg

常用 API

Vue Test Utils 提供了许多有用的 API,以下是一些常用的 API:

  • find(selector): 查找匹配选择器的第一个元素。
  • findAll(selector): 查找匹配选择器的所有元素。
  • trigger(eventType, eventData): 触发组件的事件。
  • setProps(props): 设置组件的属性。
  • setData(data): 设置组件的数据。
  • text(): 获取组件的文本内容。
  • html(): 获取组件的 HTML 代码。
示例:测试 TodoList 组件

假设我们有一个 TodoList.vue 组件:

<template>
  <div>
    <div v-for="todo in todos" :key="todo.id" data-test="todo">
      {{ todo.text }}
    </div>
    <form data-test="form" @submit.prevent="createTodo">
      <input data-test="new-todo" v-model="newTodo" />
    </form>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
const newTodo = ref('');
const todos = ref([
  {
    id: 1,
    text: 'Learn Vue.js 3',
    completed: false
  }
]);

function createTodo() {
  todos.value.push({
    id: 2,
    text: newTodo.value,
    completed: false
  });
  newTodo.value = '';
}
</script>

接下来,我们编写测试代码:

import { shallowMount } from '@vue/test-utils';
import TodoList from '@/components/TodoList.vue';

test('测试新增待办事项', async () => {
  const wrapper = shallowMount(TodoList);
  const todo = wrapper.get('[data-test="todo"]');
  expect(todo.text()).toBe('Learn Vue.js 3');

  // 模拟新增待办事项
  await wrapper.get('[data-test="new-todo"]').setValue('New To Do Item');
  await wrapper.get('[data-test="form"]').trigger('submit');

  // 断言新增的待办事项
  expect(wrapper.findAll('[data-test="todo"]')).toHaveLength(2);
});
测试快照

生成测试快照代码如下:

expect(wrapper.element).toMatchSnapshot();
配置 Jest

如果你使用的是 Vue CLI 创建的项目,默认配置的 Jest 只检查 .spec 文件。如果想要检测 .test 类型的文件,需要在 jest.config.js 中进行配置:

module.exports = {
  // ...
  testMatch: [
    '**/tests/**/*.[jt]s?(x)',
    '**/?(*.)+(spec|test).[jt]s?(x)'
  ]
};
更多示例
示例一:隐藏消息组件

组件代码:

<template>
  <div>
    <label htmlFor="toggle">显示说明</label>
    <input id="toggle" type="checkbox" :checked="showMessage" @click="showMessage = !showMessage" />
    <div id="showMessage">
      <slot v-if="showMessage"></slot>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
const showMessage = ref(false);
</script>

测试代码:

import { shallowMount } from '@vue/test-utils';
import HiddenMessage from '@/components/HiddenMessage.vue';

test('正确的渲染出来', () => {
  const wrapper = shallowMount(HiddenMessage);
  expect(wrapper.find('label').text()).toBe('显示说明');
  expect(wrapper.find('input').attributes('type')).toBe('checkbox');
});

test('默认不显示信息', () => {
  const wrapper = shallowMount(HiddenMessage);
  expect(wrapper.find('#showMessage').exists()).toBe(true);
  expect(wrapper.find('#showMessage').text()).toBe('');
});

test('点击复选框之后能够显示信息', async () => {
  const wrapper = shallowMount(HiddenMessage, {
    slots: {
      default: '<p>这是一段说明文字</p>'
    }
  });
  const checkbox = wrapper.find('input');
  await checkbox.trigger('click');
  expect(wrapper.find('#showMessage').text()).toBe('这是一段说明文字');
  await checkbox.trigger('click');
  expect(wrapper.find('#showMessage').text()).toBe('');
});
示例二:登录组件

组件代码:

<template>
  <div>
    <form @submit.prevent="handleSubmit">
      <div>
        <label for="usernameInput">Username</label>
        <input id="usernameInput" v-model="username" />
      </div>
      <div>
        <label for="passwordInput">Password</label>
        <input id="passwordInput" type="password" v-model="password" />
      </div>
      <button type="submit">Submit{{ state.loading ? '...' : null }}</button>
    </form>
    <div v-if="state.error" role="alert">{{ state.error }}</div>
    <div v-if="state.resolved" role="alert">Congrats! You're signed in!</div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

interface LoginState {
  resolved: boolean;
  loading: boolean;
  error: string | null;
}

const username = ref('');
const password = ref('');
const state = ref<LoginState>({
  resolved: false,
  loading: false,
  error: null,
});

function handleSubmit() {
  state.value.loading = true;
  state.value.resolved = false;
  state.value.error = null;

  window.fetch('/api/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      username: username.value,
      password: password.value,
    }),
  })
    .then((r) =>
      r.json().then((data) => (r.ok ? data : Promise.reject(data)))
    )
    .then(
      (user) => {
        state.value.loading = false;
        state.value.resolved = true;
        state.value.error = null;
        localStorage.setItem('token', user.token);
      },
      (error) => {
        state.value.loading = false;
        state.value.resolved = false;
        state.value.error = error.message;
      }
    );
}
</script>

测试代码:

import { shallowMount } from '@vue/test-utils';
import Login from '@/components/Login.vue';

const fakeUserResponse = { token: 'fake_user_token' };
window.fetch = jest.fn().mockResolvedValue({
  ok: true,
  json: () => Promise.resolve(fakeUserResponse),
});

afterEach(() => {
  window.localStorage.removeItem('token');
});

test('请求成功', async () => {
  const wrapper = shallowMount(Login);

  await wrapper.find('#usernameInput').setValue('xiejie');
  await wrapper.find('#passwordInput').setValue('123456');
  await wrapper.find('form').trigger('submit');

  await wrapper.vm.$nextTick();
  await new Promise((resolve) => setTimeout(resolve, 100));

  expect(window.localStorage.getItem('token')).toEqual(fakeUserResponse.token);
  expect(wrapper.find('[role="alert"]').text()).toMatch(/Congrats/i);
});

test('请求失败', async () => {
  window.fetch = jest.fn().mockResolvedValue({
    ok: true,
    json: () =>
      Promise.reject({
        message: '服务器内部错误',
      }),
  });

  const wrapper = shallowMount(Login);

  await wrapper.find('#usernameInput').setValue('xiejie');
  await wrapper.find('#passwordInput').setValue('123456');
  await wrapper.find('form').trigger('submit');

  await wrapper.vm.$nextTick();
  await new Promise((resolve) => setTimeout(resolve, 100));

  expect(window.localStorage.getItem('token')).toBeNull();
  expect(wrapper.find('[role="alert"]').text()).toMatch('服务器内部错误');
});

总结

本节介绍了如何使用 Vue Test Utils 测试 Vue 组件。Vue Test Utils 提供了一系列强大的 API,帮助开发者模拟用户操作、查询组件和断言测试结果,从而确保 Vue 应用程序的稳定性和可靠性。通过合理的测试,可以捕获潜在的问题,提高代码质量。


http://www.kler.cn/a/381969.html

相关文章:

  • WAPI认证过程如何实现?
  • stm32引脚PB3、PB4、PA15作为普通IO口用时,需要先解除调试端口复用
  • 基于SSM+uniapp的营养食谱系统+LW参考示例
  • 【数据结构与算法】LeetCode: 贪心算法
  • 【用Rust写CAD】前言
  • 【MATLAB源码-第208期】基于matlab的改进A*算法和传统A*算法对比仿真;改进点:1.无斜穿障碍物顶点2.删除中间多余节点,减少转折。
  • ONLYOFFICE 8.2深度体验:高效协作与卓越性能的完美融合
  • 学习threejs,将多个网格合并成一个网格
  • spring-第十三章 AOP
  • 【强化学习理论】基于策略的强化学习——深度确定性策略梯度算法
  • Python内置模块-Json:轻松处理数据交换的艺术
  • 项目实战使用gitee
  • Golang | Leetcode Golang题解之第538题把二叉搜索树转换为累加树
  • 质数的和与积
  • 什么是ajax,为什么使用ajax?ajax都有哪些优点和缺点?
  • 「Mac畅玩鸿蒙与硬件24」UI互动应用篇1 - 灯光控制小项目
  • 恢复Ubuntu+Windows10双系统安装前状态及分区还原详细步骤
  • L5.【LeetCode笔记】移除链表元素
  • 如何修改远程分支?修改了会影响什么?
  • python中t是什么意思