前端单元测试是怎么做的?

news2024/11/29 8:54:30

为什么要做单元测试

1. 执行单元测试,就是为了证明这段代码的行为和我们期望的一致

2. 进行充分的单元测试,是提高软件质量,降低开发成本的必由之路

3. 在开发人员做出修改后进行可重复的单元测试可以避免产生那些令人不快的负作用

怎么去设计单元测试

·理解这个单元原本要做什么(倒推出一个概要的规格说明(阅读那些程序代码和注释))

· 画出流程图

· 组织对这个概要规格说明的走读(Review),以确保对这个单元的说明没有基本的错误

· 设计单元测试

在实践工作中,进行了完整计划的单元测试和编写实际的代码所花费的精力大致上是相同的

两个常用的单元测试方法论:

· TDD(Test-driven development):测试驱动开发

· BDD(Behavior-driven development):行为驱动开发

前端与单元测试

如何对前端代码做单元测试

通常是针对函数、模块、对象进行测试

至少需要三类工具来进行单元测试:

*测试管理工具

*测试框架:就是运行测试的工具。通过它,可以为 JavaScript 应用添加测试,从而保证代码的质量

*断言库

测试浏览器

测试覆盖率统计工具

测试框架选择

Jasmine:Behavior-Drive development(BDD)风格的测试框架,在业内较为流行,功能很全面,自带 asssert、mock 功能

Qunit:该框架诞生之初是为了 jquery 的单元测试,后来独立出来不再依赖于 jquery 本身,但是其身上还是脱离不开 jquery 的影子

Mocha:node 社区大神 tj 的作品,可以在 node 和 browser 端使用,具有很强的灵活性,可以选择自己喜欢的断言库,选择测试结果的 report

Jest:来自于 facebook 出品的通用测试框架,Jest 是一个令人愉快的 JavaScript 测试框架,专注于简洁明快。他适用但不局限于使用以下技术的项目:Babel, TypeScript, Node, React, Angular, Vue

如何编写测试用例(Jest + Enzyme)

通常测试文件名与要测试的文件名相同,后缀为.test.js,所有测试文件默认放在__test__文件夹中。

describe块之中,提供测试用例的四个函数:before()、after()、beforeEach()和 afterEach()。它们会在指定时间执行(如果不需要可以不写)。

测试文件中应包括一个或多个describe, 每个 describe 中可以有一个或多个it,每个describe中可以有一个或多个expect。

describe 称为"测试套件"(test suite),it 块称为"测试用例"(test case)。

expect就是判断源码的实际执行结果与预期结果是否一致,如果不一致就抛出一个错误。

所有的测试都应该是确定的。 任何时候测试未改变的组件都应该产生相同的结果。 你需要确保你的快照测试与平台和其他不相干数据无关。

基础模板

 describe('加法函数测试', () => {
    before(() => {
      // 在本区块的所有测试用例之前执行
    });
    after(() => {
      // 在本区块的所有测试用例之后执行
    });
    beforeEach(() => {
      // 在本区块的每个测试用例之前执行
    });
    afterEach(() => {
      // 在本区块的每个测试用例之后执行
    });
    it('1加1应该等于2', () => {
      expect(add(1, 1)).toBe(2);
    });
    it('2加2应该等于4', () => {
      expect(add(2, 2)).toBe(42);
    });
  });

常用的测试

组件中的方法测试:

it('changeCardType', () => {
    let component = shallow(<Card />);
    expect(component.instance().cardType).toBe('initCard');
    component.instance().changeCardType('testCard');
    expect(component.instance().cardType).toBe('testCard');
  });

模拟事件测试

通过 Enzyme 可以在这个返回的 dom 对象上调用类似 jquery 的 api 进行一些查找操作,还可以调用 setProps 和 setState 来设置 props 和 state,也可以用 simulate 来模拟事件,触发事件后,去判断 props 上特定函数是否被调用,传参是否正确;组件状态是否发生预料之中的修改;某个 dom 节点是否存在是否符合期望。

 it('can save value and cancel', () => {
    const value = 'edit';
    const { wrapper, props } = setup({
      editable: true,
    });
    wrapper.find('input').simulate('change', { target: { value } });
    wrapper.setProps({ status: 'save' });
    expect(props.onChange).toBeCalledWith(value);
  });

使用 snapshot 进行 UI 测试:

it('App -- snapshot', () => {
const renderedValue = renderer.create(<App />).toJSON();
expect(renderedValue).toMatchSnapshot();
});

真实用例分析(组件)

写一个单元测试你需要这样做

1. 看代码,熟悉待测试模块的功能和作用

2. 设计测试用例必须覆盖到组件的各种情况

3. 对错误情况的测试

通常测试文件名与要测试的文件名相同,后缀为.test.js,所有测试文件默认放在test文件夹中,一般测试文件包含下列内容:

·全局设置:一些前置配置,mock 的全局或第三方方法、进行一些重复的组件初始化工作,,当多个测试用例有相同的初始化组件行为时,可以在这里进行挂载和销毁

· UI 测试:为组件打快照,第一次运行测试命令会在目录下生成一个组件的 DOM 节点快照,在之后的测试命令中会与快照文件进行 diff 对照,避免在后面对组件进行了非期望的 UI 更改

· 关键行为:验证组件的基本行为(如:Checkbox 组件的勾选行为)

· 事件:测试各种事件的触发

· 属性:测试传入不同属性值是否得到与期望一致的结果

accordion 组件

// accordion.test.tsx
  import { afterEach, beforeEach, describe, expect, jest, test } from '@jest/globals';
  import Enzyme, { mount } from 'enzyme';
  import Adapter from 'enzyme-adapter-react-16';
  import toJSON from 'enzyme-to-json';
  import JestMock from 'jest-mock';
  import React from 'react';
  import { Accordion } from '..';
  Enzyme.configure({ adapter: new Adapter() }); // 需要根据项目的react版本来配置适配
  describe('Accordion', () => {
    // 测试套件,通过 describe 块来将测试分组
    let onChange: JestMock.Mock<any, any>; // Jest 提供的mock 函数,擦除函数的实际实现、捕获对函数的调用
    let wrapper: Enzyme.ReactWrapper;
    beforeEach(() => {
      // 在运行测试前做的一些准备工作
      onChange = jest.fn();
      wrapper = mount(
        <Accordion onChange={onChange}>
          <Accordion.Item name='one' header='one'>
            two
          </Accordion.Item>
          <Accordion.Item name='two' header='two' disabled={true}>
            two
          </Accordion.Item>
          <Accordion.Item name='three' header='three' showIcon={false}>
            three
          </Accordion.Item>
          <Accordion.Item name='four' header='four' active={true} icons={['custom']}>
            four
          </Accordion.Item>
        </Accordion>
      );
    });
    afterEach(() => {
      // 在运行测试后进行的一些整理工作
      wrapper.unmount();
    });
    // UI快照测试,确保你的UI不会因意外改变
    test('Test snapshot', () => {
      // 测试用例,需要提供详细的测试用例描述
      expect(toJSON(wrapper)).toMatchSnapshot();
    });
    // 事件测试
    test('should trigger onChange', () => {
      wrapper.find('.qtc-accordion-item-header').first().simulate('click');
      expect(onChange.mock.calls.length).toBe(1);
      expect(onChange.mock.calls[0][0]).toBe('one');
    });
    // 关键逻辑测试
    //点击头部触发展开收起
    test('should expand and collapse', () => {
      wrapper.find('.qtc-accordion-item-header').at(2).simulate('click');
      expect(wrapper.find('.qtc-accordion-item').at(2).hasClass('active')).toBeTruthy();
    });
    // 配置disabled时不可展开
    test('should not trigger onChange when disabled', () => {
      wrapper.find('.qtc-accordion-item-header').at(1).simulate('click');
      expect(onChange.mock.calls.length).toBe(0);
    });
    // 对所有的属性配置进行测试
    // 是否展示头部左侧图标
    test('hide icon', () => {
      expect(wrapper.find('.qtc-accordion-item-header').at(2).children().length).toBe(2);
    });
    // 自定义图标
    test('custom icon', () => {
      const customIcon = wrapper.find('.qtc-accordion-item-header').at(3).children().first();
      expect(customIcon.getDOMNode().innerHTML).toBe('custom');
    });
    // 是否可展开多项
    test('single expand', () => {
      onChange = jest.fn();
      wrapper = mount(
        <Accordion multiple={false} onChange={onChange}>
          <Accordion.Item name='1'>1</Accordion.Item>
          <Accordion.Item name='2'>2</Accordion.Item>
        </Accordion>
      );
      wrapper.find('.qtc-accordion-item-header').at(0).simulate('click');
      wrapper.find('.qtc-accordion-item-header').at(1).simulate('click');
      expect(wrapper.find(Accordion).state().activeNames).toEqual(new Set(['2']));
    });
    test('mutiple expand', () => {
      onChange = jest.fn();
      wrapper = mount(
        <Accordion multiple={true} onChange={onChange}>
          <Accordion.Item name='1'>1</Accordion.Item>
          <Accordion.Item name='2'>2</Accordion.Item>
        </Accordion>
      );
      wrapper.find('.qtc-accordion-item-header').at(0).simulate('click');
      wrapper.find('.qtc-accordion-item-header').at(1).simulate('click');
      expect(wrapper.find(Accordion).state().activeNames).toEqual(new Set(['1', '2']));
    });
  });

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取  

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLOv8中的C2f的详细解读

C2f的结构图,看不懂没关系,继续往下看,一定会看懂的!!!首先是C2f的逻辑代码: class C2f(nn.Module):# CSP Bottleneck with 2 convolutionsdef __init__(self, c1, c2, n=1, shortcut=

排序算法的比较与java实现

冒泡排序 基本思想: 比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。 对每一对相邻元素作同样的工作&#xff0c;从开始第一对到结尾的最后一对。在这一点&#xff0c;最后的元素应该会是最大的数。 针对所有的元素重复以上的步骤&#xff0c;除了最后一个。…

网络安全大厂常见面试题

以下为网络安全各个方向涉及的面试题&#xff0c;星数越多代表问题出现的几率越大&#xff0c;祝各位都能找到满意的工作。 注&#xff1a;本套面试题&#xff0c;已整理成pdf文档&#xff0c;但内容还在持续更新中&#xff0c;因为无论如何都不可能覆盖所有的面试问题&#xf…

原神3.2服务端架设服务器搭建教程ubuntu系统(保姆级)

原神3.2服务端架设服务器搭建教程ubuntu系统&#xff08;保姆级&#xff09; 大家好&#xff0c;我是艾西今天跟大家分享下原神3.2服务端架设ubuntu系统实操教程 准备阶段&#xff1a;服务器一台 32h32g起、服务端、客户端、服务器装Ubuntu20.04系统 特别强调&#xff1a;ja…

怎样通过font属性控制CSS字体样式?

为了更方便地控制网页中各种各样的字体&#xff0c;CSS提供了一系列的字体样式属性&#xff0c;具体如下。 (1)font-size属性&#xff1a;字号 font-size属性用于设置字号&#xff0c;该属性的属性值可以为像素值、百分比数值、倍率等。表3-l列举了fomt-size属性常用的属性值…

回炉重造十四---微服务

微服务 1、zookeeper 1.1Zookeeper的功能 1.1.1命名服务 命名服务是分布式系统最基本的公共服务之一。在分布式系统中&#xff0c;被命名的实体通常可以是集群中的机器、提供的服务地址或远程对象等一一这些我们都可以通称他们为名字&#xff08;Name)&#xff0c;通过命名…

SpringCloud_服务调用_Ribbon概述以及使用(一)

SpringCloud_负载均衡_Ribbon(一) 概述 Ribbbon负载均衡演示 Ribbbon核心组件IRule Ribbbon负载均衡算法 概述 Ribbbon是一套客户端 负载均衡的工具 提供客户端的软件负载均衡算法和服务调用 地址&#xff1a; https://github.com/Netflix/ribbon/wiki/Getting-Started 目前这几…

java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver报错问题分析

java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver 这个错误通常意味着应用程序服务器在尝试在只读模式下访问数据库时出现了问题&#xff0c;该错误与事务处理有关。通常出现在JDBC连接对象或事务对象的创建或状态查询的过程中。 在JDBC中&#xff0c;只读事务需要…

【机器视觉2】单目相机内外参数标定

单目相机内外参数标定 1. 标定参照物概述2. 张正友平面模板标定法 1. 标定参照物概述 标定相机内外参数需要获取场景和图像间多个坐标对。场景坐标点直接从标定参照物获取&#xff0c;图像坐标点从图像中获取。 标定参照物有二维、三维等&#xff0c;如下图所示&#xff1a; …

【linux】冯诺依曼体系+操作系统

我们使用的计算机都是由一个个硬件所组成的&#xff0c;那么如何有条不紊的运行呢&#xff1f;那是因为有冯诺依曼体系约束着硬件&#xff0c;而操作系统来管理着他们&#xff0c;从而使得计算机的硬件和软件完美结合。 一、冯诺依曼体系 首先我们得了解什么是冯诺依曼体系结构…

万字长文 | ChatGPT的工作原理(一)

ChatGPT 能够自动生成一些读起来表面上甚至像人写的文字的东西&#xff0c;这非常了不起&#xff0c;而且出乎意料。但它是如何做到的&#xff1f;为什么它能发挥作用&#xff1f;我在这里的目的是大致介绍一下 ChatGPT 内部的情况&#xff0c;然后探讨一下为什么它能很好地生成…

Sharding-JDBC之绑定表(关联表)

目录 一、简介二、maven依赖三、数据库3.1、创建数据库3.2、创建表 四、配置&#xff08;二选一&#xff09;4.1、properties配置4.2、yml配置 五、实现5.1、实体层5.2、持久层5.3、服务层5.4、测试类5.4.1、保存订单数据5.4.2、查询订单详情数据&#xff08;关联表&#xff09…

拓扑排序在处理树形关系结构中的应用

Preface 偶然在QQ上的一个交流群中看到了一位群友的棘手需求。互联网开发中&#xff0c;数据的落盘存储通常在MySQL中。MySQL是一种关系型数据库&#xff0c;以“行”为基本的存储单元&#xff0c;然后通过外键等建立数据实体模型之间的联系。 但有些数据的存储&#xff0c;在…

波奇学C++:友元函数,友元类,内部类,匿名对象,优化构造

友元函数/类 &#xff1a;突破访问限定符&#xff0c;允许非同一个类的函数或者类访私有成员变量。 class A { public:A():_a(0),_b(1){cout << "A()" << endl;} private:int _a 0;int _b 1; }; void fun(const A& a) {cout << a._a <&l…

canal学习-运行canal-adapter源码并记录解决报错问题(一)

运行canal-adapter 1. 下载canal源码1.1 下载源码并安装好环境1.2 查看目录结构 2.项目运行2.1 项目打包2.2 项目打包可能遇到的问题&#xff1a;1.Failure to find com.alibaba.otter:connector.tcp:jar:jar-with-dependencies:1.1.52.com.alibaba.druid.pool.DruidDataSource…

HTTP协议与TCP协议

HTTP协议 1. HTTP有哪些⽅法&#xff1f; HTTP 1.0 标准中&#xff0c;定义了3种请求⽅法&#xff1a;GET、POST、HEAD HTTP 1.1 标准中&#xff0c;新增了请求⽅法&#xff1a;PUT、PATCH、DELETE、OPTIONS、TRACE、CONNECT 2. 各个HTTP方法的具体作用是什么&#xff1f; 方…

量子计算:揭开未来计算世界的面纱

随着科技的飞速发展&#xff0c;计算能力的提升成为人们关注的焦点之一。而在这个领域中&#xff0c;量子计算正逐渐成为备受瞩目的新星。量子计算利用了量子力学的原理&#xff0c;与传统计算方式有着根本的不同。在传统计算中&#xff0c;信息以比特的形式表示&#xff0c;而…

COMSOL光电专题第三十三期(线上),COMSOL声学(北京线下)专题线上通知

背景&#xff1a; COMSOL多物理场仿真软件以高效的计算性能和杰出的多场耦合分析能力实现了精确的数值仿真&#xff0c;已被广泛应用于各个领域的科学研究以及工程计算&#xff0c;为工程界和科学界解决了复杂的多物理场建模问题。COMSOL内嵌的声学模块可以方便地进行多孔声学…

AI技术如何助力合同智能管理?

近年来&#xff0c;合同管理领域开始大规模应用AI技术&#xff0c;今天我们来关注下AI技术如何助力合同智能管理&#xff1f; 传统的合同管理系统&#xff0c;一般都是流程管理&#xff0c;随着AI技术的快速发展&#xff0c;AI技术已经成功应用到了合同全生命周期管理的各阶段…

计算机网络 三 (数据链路层)上

数据链路层 数据链路层的概述 基本概念 数据链路层是OSI参考模型中的第二层&#xff0c;它主要负责在物理层上提供可靠的数据传输服务&#xff0c;使得相邻节点间的数据传输能够实现。 数据链路层的基本概念如下&#xff1a; 帧&#xff1a;数据链路层数据传输的基本单位是…