Vue 组件单元测试深度探索:组件交互与状态变更 专业解析和实践

在这里插入图片描述

在Vue组件单元测试中,验证组件之间的交互(如父组件与子组件、兄弟组件之间的通信)以及状态变更的正确性对于保证整个应用的协调运作至关重要。本文详细介绍了父组件向子组件传递props、子组件向父组件发送事件、兄弟组件通过共享状态(如Vuex store)进行交互、兄弟组件通过事件总线(如this.$root.$emit)进行交互等组件交互与状态变更的测试详解与举例。

一、父组件与子组件交互

1. 父组件向子组件传递props

  • 原理:Vue提供了props特性,允许父组件以属性的方式将数据传递给子组件。
  • 用法
    • 在子组件中通过props选项定义需要接收的属性,如props: ['message', 'isActive']
    • 父组件模板中使用子组件时,直接将数据作为属性传入,如<ChildComponent message="Hello" :isActive="isChildActive" />。其中,:isActive="isChildActive"表示将父组件的isChildActive数据属性绑定到子组件的isActive prop。
    • 子组件内部可以直接访问这些props作为其自身数据的一部分。

测试父组件是否正确将props传递给子组件,并检查子组件是否根据props正确渲染:

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

describe('ParentComponent', () => {
  let parentWrapper;
  let childWrapper;

  beforeEach(() => {
    parentWrapper = shallowMount(ParentComponent);
    childWrapper = parentWrapper.findComponent(ChildComponent);
  });

  afterEach(() => {
    parentWrapper.destroy();
  });

  it('passes props to child component', () => {
    expect(childWrapper.props()).toEqual({
      someProp: 'expectedValue',
      anotherProp: 42,
    });
  });

  it('updates child component when prop changes', async () => {
    parentWrapper.setProps({ someProp: 'updatedValue' });
    await parentWrapper.vm.$nextTick();

    expect(childWrapper.props('someProp')).toBe('updatedValue');
  });
});

2. 子组件向父组件发送事件

  • 原理:子组件通过$emit方法触发自定义事件,父组件监听并响应这些事件。
  • 用法
    • 在子组件内部,当需要向父组件发送信息时,使用this.$emit('eventName', eventData)触发一个自定义事件,如this.$emit('updateStatus', newStatus)
    • 父组件在使用子组件时,通过v-on@修饰符监听该事件,并在回调函数中处理传递的数据,如<ChildComponent @updateStatus="handleStatusUpdate" />。这里的handleStatusUpdate(newStatus)是父组件的方法,用于接收并处理子组件发出的事件数据。

测试子组件是否正确触发事件,并验证父组件是否正确接收并响应事件:

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

describe('ParentComponent', () => {
  let parentWrapper;
  let childWrapper;

  beforeEach(() => {
    parentWrapper = shallowMount(ParentComponent);
    childWrapper = parentWrapper.findComponent(ChildComponent);
  });

  afterEach(() => {
  parentWrapper.destroy();
  });

  it('receives and handles event emitted by child component', async () => {
    const parentEventHandlerSpy = jest.spyOn(parentWrapper.vm, 'onChildEvent');

    childWrapper.vm.$emit('childEvent', { data: 'eventData' });
    await parentWrapper.vm.$nextTick();

    expect(parentEventHandlerSpy).toHaveBeenCalledWith({ data: 'eventData' });
    expect(parentWrapper.vm.someState).toBe('expectedStateAfterEvent');
  });
});

二、兄弟组件交互

1. 通过共享状态(如Vuex store)进行交互

  • 原理:Vuex是一个专为Vue应用设计的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
  • 用法
    • 首先,在项目中安装并配置Vuex。
    • 创建store文件夹,编写index.js文件,定义state、mutations、actions、getters等核心模块。
    • 在需要共享状态的兄弟组件中:
      • 通过mapStatemapGetters辅助函数将store中的状态或计算属性映射为局部计算属性。
      • 通过this.$store.commit('mutationName', payload)触发mutations更新状态。
      • 通过this.$store.dispatch('actionName', payload)触发异步操作或包含复杂逻辑的操作。
      • 兄弟组件间无需直接相互引用,只需对共享状态进行读取和修改,即可实现间接交互。

测试兄弟组件是否正确读取和修改共享状态,并根据状态变更正确更新自身:

import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import ComponentA from '@/components/ComponentA.vue';
import ComponentB from '@/components/ComponentB.vue';

const localVue = createLocalVue();
localVue.use(Vuex);

describe('ComponentA and ComponentB interaction via Vuex store', () => {
  let store;
  let componentAWrapper;
  let componentBWrapper;

  beforeEach(() => {
    store = new Vuex.Store({
      state: { sharedData: 'initialValue' },
      mutations: { updateSharedData(state, newValue) { state.sharedData = newValue; } },
      actions: { updateSharedDataAction(context, newValue) { context.commit('updateSharedData', newValue); } },
    });

    componentAWrapper = shallowMount(ComponentA, { localVue, store });
    componentBWrapper = shallowMount(ComponentB, { localVue, store });
  });

  afterEach(() => {
    componentAWrapper.destroy();
    componentBWrapper.destroy();
  });

  it('reads shared state correctly', () => {
    expect(componentAWrapper.vm.sharedData).toBe('initialValue');
    expect(componentBWrapper.vm.sharedData).toBe('initialValue');
  });

  it('updates shared state through actions/mutations and reacts to changes', async () => {
    componentAWrapper.vm.updateSharedData('newValue');
    await componentAWrapper.vm.$nextTick();
    await componentBWrapper.vm.$nextTick();

    expect(store.state.sharedData).toBe('newValue');
    expect(componentAWrapper.vm.sharedData).toBe('newValue');
    expect(componentBWrapper.vm.sharedData).toBe('newValue');
  });
});

2. 通过事件总线(如this.$root.$emit)进行交互

  • 原理:利用Vue实例的事件系统,通过一个公共的祖先组件(通常是根实例vm.$root)作为事件总线,使得兄弟组件间能够通过触发和监听全局事件进行通信。
  • 用法
    • 发送事件的兄弟组件使用this.$root.$emit('eventName', eventData)触发全局事件。
    • 接收事件的兄弟组件使用this.$root.$on('eventName', eventHandler)监听全局事件。当事件被触发时,eventHandler回调函数会被调用,处理传递的eventData

测试兄弟组件是否正确通过事件总线发送和接收事件,并据此更新自身状态:

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

describe('ComponentA and ComponentB interaction via event bus', () => {
  let componentAWrapper;
  let componentBWrapper;

  beforeEach(() => {
    componentAWrapper = shallowMount(ComponentA);
    componentBWrapper = shallowMount(ComponentB);
  });

  afterEach(() => {
    componentAWrapper.destroy();
    componentBWrapper.destroy();
  });

  it('sends event from one component and receives it in another', async () => {
    const eventHandlerSpy = jest.spyOn(componentBWrapper.vm, 'onCustomEvent');

    componentAWrapper.vm.sendCustomEvent('eventData');
    await componentBWrapper.vm.$nextTick();

    expect(eventHandlerSpy).toHaveBeenCalledWith('eventData');
    expect(componentBWrapper.vm.someState).toBe('expectedStateAfterEvent');
  });
});

三、组件内部状态变更

测试组件在接收到用户输入、响应事件或执行内部逻辑时,其内部状态是否正确变更:

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

describe('MyComponent', () => {
  let wrapper;

  beforeEach(() => {
    wrapper = shallowMount(MyComponent);
  });

  afterEach(() => {
    wrapper.destroy();
  });

  it('updates internal state on user input', async () => {
    const inputElement = wrapper.find('input[type="text"]');
    inputElement.setValue('newInputValue');

    await wrapper.vm.$nextTick();

    expect(wrapper.vm.internalState.inputValue).toBe('newInputValue');
    expect(wrapper.find('.displayed-value').text()).toBe('newInputValue');
  });

  it('changes state when an async action completes', async () => {
    const fetchMock = jest.fn().mockResolvedValue({ data: 'asyncData' });
    wrapper.setMethods({ fetchData: fetchMock });

    wrapper.vm.fetchData();
    await wrapper.vm.$nextTick();

    expect(fetchMock).toHaveBeenCalled();
    expect(wrapper.vm.internalState.asyncData).toBe('asyncData');
  });
});

更多组件单元测试《Vue 组件单元测试深度探索:细致解析与实战范例大全》

总结

通过对组件间的交互(包括父组件与子组件、兄弟组件之间的通信)以及组件内部状态变更进行详尽的单元测试,可以确保Vue应用中的组件能够协同工作,正确响应外部输入和内部逻辑,从而维持整个应用的稳定性和一致性。测试时应关注props传递、事件触发与接收、状态读取与更新等关键环节,确保在各种交互场景下组件行为符合预期。同时,利用测试工具提供的断言和模拟功能,使得测试用例既准确又易于维护。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/579711.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

xfce4 panel 不能显示QQ,钉钉的状态图标

有一段时间不能显示了&#xff0c;之前刚装完系统的时候很长时间内都是好的&#xff0c;所以刚开始肯定是支持显示这些状态图标的。就是因为不能显示的原因&#xff0c;所以还装了lxQt桌面&#xff0c;这个桌面确实不错。不过还是有时会怀念xfce4&#xff0c;想看看能不能解决这…

Odoo:全球排名第一的免费开源PLM管理系统介绍

概述 利用开源智造OdooPLM产品生命周期管理应用&#xff0c;重塑创新 实现产品生命周期管理数字化&#xff0c;高效定义、开发、交付和管理创新的可持续产品&#xff0c;拥抱数字化供应链。 通过开源智造基于Odoo开源技术平台打造数字化的产品生命周期管理&#xff08;PLM&am…

Java UUID 类中的 getMostSignificantBits 和 leastSignificantBits 方法

getMostSignificantBits() 方法的一些介绍&#xff1a; getMostSignificantBits 这个方法主要用于在 UUID 中获取高64 位的有效位后返回 Long 数据类型。 在获取最高有效位时不会引发异常。 对应的还有一个 getLeastSignificantBits() 方法。 这个方式是从 UUID 中获取低 6…

阳光能源,创造永远:光模块的未来”:随着大数据、区块链、云计算和5G的发展,光模块成为满足不断增长的数据流量需求的关键技术

光模块的类型介绍&#xff1a; 为了适应不同的应用需求&#xff0c;不同参数和功能的光模块应运而生。光模块的分类方式及类型详见如下&#xff1a; &#x1f50e;封装形式&#x1f50d;&#xff1a; &#x1f4e3;&#x1f4e2;光模块按照封装形式来分有以下几种常见类型&a…

如何将本地Android studio项目上传到GitHub

操作步骤&#xff1a; 1、在GitHub上创建账户 2、在androd studio中添加上述创建的GitHub账号 3、在android studio上找到"share project on GitHub"&#xff0c;点击此选项上传当前项目到GitHub 上传成功后&#xff0c;会在GitHub上创建默认仓库repository 注&a…

LeetCode - LCR 008.长度最小的子数组

一. 题目链接 LeetCode - 209. 长度最小的子数组 二. 思路分析 由于此问题分析的对象是「⼀段连续的区间」&#xff0c;因此可以考虑「滑动窗口」的思想来解决这道题。 让滑动窗口满足&#xff1a;从 i 位置开始&#xff0c;窗口内所有元素的和小于target &#xff08;那么当…

图像处理到神经网络:线性代数的跨领域应用探索

作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a;码上找工作 作者专栏每日更新&#xff1a; LeetCode解锁1000题: 打怪升级之旅 python数据分析…

Python爬虫--Scrapy框架安装

Scrapy框架安装 &#xff0c; Scrapy 是 Python 领域专业的爬虫开发框架&#xff0c;已经完成爬虫程序的大部分通用工具 它使用了 Twisted 异步网络库来处理网络通讯。整体架构大致如下 第一步&#xff1a;挂小灰机或者将要安装的文件下载到本地 Scrapy 框架安装踩坑中 为什…

ubuntu的镜像源+bionic版本

首先第一步 查找和你自己ubuntu版本匹配的版本号 匹配代号如下 在终端输入lsb_release -a查看自己系统上的版本号 可以看到我这个版本号的代号是bionic。 每个版本的镜像文件都是有规律的。 bionic版本的源如下 # 阿里源 deb http://mirrors.aliyun.com/ubuntu/ bionic ma…

前端用a标签实现静态资源文件(excel/word/pdf)下载

接上文实现的 前端实现将二进制文件流&#xff0c;并下载为excel文件后&#xff0c; 实际项目中一般都会有一个模版下载的功能&#xff0c;一般都由服务端提供一个下载接口&#xff0c;返回文件流或url地址&#xff0c;然后前端再处理成对应需要的类型的文件。 但是&#xff…

HTML5(1)

目录 一.HTML5(超文本&#xff08;链接&#xff09;标记&#xff08;标签<>&#xff09;语言) 1.开发环境&#xff08;写代码&#xff0c;看效果&#xff09; 2.vscode 使用 3.谷歌浏览器使用 4.标签语法 5.HTML基本骨架&#xff08;网页模板&#xff09; 6.标签的…

排序 “肆” 之归并排序

1. 归并排序 1.1 原理介绍 归并排序的基本原理是将一个未排序的数组分解为较小的子数组&#xff0c;然后递归地对这些子数组进行排序&#xff0c;最后再将排好序的子数组合并成一个有序数组。其核心操作是将一维数组中前后相邻的两个有序序列归并为一个有序序列。 其主要步骤包…

【区块链】椭圆曲线数字签名算法(ECDSA)

本文主要参考&#xff1a; 一文读懂ECDSA算法如何保护数据 椭圆曲线数字签名算法 1. ECDSA算法简介 ECDSA 是 Elliptic Curve Digital Signature Algorithm 的简称&#xff0c;主要用于对数据&#xff08;比如一个文件&#xff09;创建数字签名&#xff0c;以便于你在不破坏它…

【Flutter】GetX

前言 状态管理 / 路由管理 / 依赖管理 这三部分之间存在联系 参考文章 建议看官网文章&#xff0c;很详细 &#xff0c;pub.dev搜索get pub.dev的文档 状态管理文章相关链接 状态管理 案例 实现一个计算器&#xff0c;运用GetX去管理它 构建界面 构建一个计算器界面 …

基于SpringBoot的“房产销售平台”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“房产销售平台”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统整体模块图 登录窗口界面 房源信息管理窗口界…

解决HttpServletRequest中的InputStream/getReader只能被读取一次的问题

一、事由 由于我们业务接口需要做签名校验&#xff0c;但因为是老系统了签名规则被放在了Body里而不是Header里面&#xff0c;但是我们不能在每个Controller层都手动去做签名校验&#xff0c;这样不是优雅的做法&#xff0c;然后我就写了一个AOP&#xff0c;在AOP中实现签名校…

Linux--进程控制(2)--进程的程序替换(夺舍)

目录 进程的程序替换 0.相关函数 1.先看现象 2.解释原理 3.将代码改成多进程版 4.使用其它的替换函数&#xff0c;并且认识函数参数的含义 5.其它 进程的程序替换 0.相关函数 关于进程替换我们需要了解的6个函数&#xff1a; 函数解释&#xff1a; 这些函数如果调用成功则…

【Web UI自动化】Python+Selenium 环境配置

安装Python 官网地址&#xff1a;https://www.python.org/&#xff0c;Downloads菜单下选择适合自己的系统版本&#xff0c;我的是Windows。 点击进入以后&#xff0c;可以看到当前最新版本。 点击上面的链接&#xff0c;页面下滑&#xff0c;找到下载链接&#xff0c;根据…

网站推荐——文本对比工具

在线文字对比工具-BeJSON.com 文本对比/字符串差异比较 - 在线工具 在线文本对比-文本内容差异比较-校对专用

OpenCV C++实现区域面积筛选以及统计区域个数

目录 1、背景介绍 2、代码实现 2.1 获取原图 2.1.1 区域图像imread 2.1.2 具体实现 2.2 获取图像大小 2.3 阈值分割 2.3.1 阈值分割threshold 2.3.2 具体实现 2.4 区域面积筛选 2.4.1 获取轮廓findContours 2.4.2 获取轮廓面积contourArea 2.4.3 填充区域fil…