Jest进阶:测试 Vue 组件

news/2024/11/6 11:08:55 标签: vue.js, 前端, javascript, 前端框架

在 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>javascript">
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">javascript">
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">javascript">
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">javascript">
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.niftyadmin.cn/n/5740805.html

相关文章

《C++ 网络编程:高效实现 TCP/IP 与 UDP 通信》

在当今数字化时代&#xff0c;网络编程在各个领域都有着至关重要的地位。对于 C开发者来说&#xff0c;掌握高效的网络编程方法&#xff0c;特别是 TCP/IP 和 UDP 通信的实现&#xff0c;是一项极具价值的技能。这篇文章将深入探讨在 C中实现高效网络编程的要点和技巧。 一、网…

前端关闭控制台打印信息

vite配置文件里关闭打印方法 import { defineConfig } from "vite"; import react from "vitejs/plugin-react-swc"; import removeConsole from "vite-plugin-remove-console"; // https://vitejs.dev/config/ export default defineConfig({p…

Neo4j Cypher WHERE子句详解 - 初学者指南

Neo4j Cypher WHERE子句详解 - 初学者指南 前言1. WHERE子句基础1.1 WHERE子句的本质1.2 示例数据 2. 基本用法2.1 节点属性过滤2.2 关系属性过滤 3. 高级过滤技巧3.1 字符串匹配3.2 正则表达式3.3 属性存在性检查 4. 列表和范围操作4.1 IN操作符4.2 范围查询 5. 空值处理5.1 默…

Spring 设计模式之责任链模式

Spring 设计模式之责任链模式 责任链模式用到的场景java举例 责任链模式 责任链模式&#xff08;Chain of Responsibility&#xff09;是一种行为设计模式&#xff0c;它允许你将请求沿着处理者链进行传递。 每个处理者可以对请求进行处理&#xff0c;也可以将请求传递给链中的…

学习python中的pandas有没有好的教程推荐?

看到Pandas我可就不困了&#xff0c;这是我用的最多的工具。 Pandas作为Python数科领域最顶级的库之一&#xff0c;就像excel之于office&#xff0c;是处理数据必备工具。 Pandas的学习教程自然不会少&#xff0c;在Github上搜索Pandas&#xff0c;会出现超过6万个项目&#…

Node.js——fs模块-文件夹操作

1、借助Node.js的能力&#xff0c;我们可以对文件夹进行创建、读取、删除等操作 2、方法 方法 说明 mkdir/mkdirSync 创建文件夹 readdir/readdirSync 读取文件夹 rmdir/rmdirSync 删除文件夹 3、语法 其余的方法语法类似 本文的分享到此结束&#xff0c;欢迎大家评论区…

VMWARE ESXI VMFS阵列故障 服务器数据恢复

1&#xff1a;河南用户一台DELL R740 3块2.4T硬盘组的RAID5&#xff0c;早期坏了一个盘没有及时更换&#xff0c;这次又坏了一个&#xff0c;导致整组RAID5处于数据丢失的状态&#xff0c; 2&#xff1a;该服务器装的是VMware ESXI 6.7&#xff0c;用户把3块硬盘寄过来进行数据…

Chromium Mojo(IPC)进程通信演示 c++(4)

122版本自带的mojom通信例子仅供学习参考&#xff1a; codelabs\mojo_examples\01-multi-process 其余定义参考文章&#xff1a; Chromium Mojo(IPC)进程通信演示 c&#xff08;2&#xff09;-CSDN博客 01-mojo-browser.exe 与 01mojo-renderer.exe进程通信完整例子。 一、…