单元测试到底测什么,怎么测?我来告诉你

前言:
以国内互联网的开发节奏,在前端业务项目中全面覆盖单元测试有时显得不太可行,主要是因为以下这些绊脚石:

UI 交互复杂,路径难以覆盖全面
工期紧,开发对实践 TDD,BDD 所带来的长远效益没有信心
产品经理们时不时打着「敏捷开发」的旗号改需求,使得刚刚辛辛苦苦写完的测试脚本完全作废
在这样的处境下,一味强调单元测试的逻辑覆盖率是没有太大意义的,明确在哪里应用单测的能取得最大的边际效益是更有意义的事情。

以下笔者根据自己的一些在单测的实战经验,列出了三项关于「单元测试应该测什么」的观点并附以一些例子与大家交流:

单元测试并非测试的全部
拿来主义地对待单元测试

单测只是一种局部模块测试,是诸多测试方案中的一种,认识到这一点可以避免我们为了测试而测试,或者为了指标而测试。

同时也应该认识到单测本身的覆盖能力也是有限的,全部用例的 PASS 和 100% 的覆盖率都不能保证被测试模块的所有逻辑路径都有正确的行为。

是否对一个模块使用单元测试往往取决于这个模块的逻辑稳定性和业务类型

例如对于一个底层 npm 包项目,单元测试几乎是他唯一的代码质量保障手段,这时就应该尽可能通过单元测试验证它在各种应用场景下的行为是否符合预期,来最低成本地保证它每次发包和更新的质量。对这类项目,彻底应用 BDD 开发模式也会获得越来越高的开发效率收益。

而对于一个功能复杂的 UI 组件,除了单元测试,还有 E2E 测试,自动化回归测试,QA 手动测试(:blush:)来保障它的代码质量。此时使用单元测试的边际效益可能不是最高的,可以考虑通过别的手段来回归它的逻辑。也可以考虑在初版功能验证上线后通过快照测试(snapshot)来回归验证每一次迭代的逻辑。

边界环境的模拟
让模块穿梭时空

单测的一个很重要的意义是帮助我们在开发阶段模拟出 QA 手动测试(:blush:)甚至线上使用场景下都不易触达的边界场景,如:

模拟个别浏览器下的 JS 版本
模拟某个 URL 状态
模拟某种本地缓存状态
模拟不同时区下的情形
模拟时间过了一个小时(这几乎只有单元测试能够做到)
等等

使用这类模拟对模块进行单元测试的边际效益是极高的,往往比 QA 去作等价的模拟快得多。

比如下面这段脚本,通过 jest 的 timer mock 能力,实现了对 expire 函数的测试:

const expire = (callback) => setTimeout(callback, 60000); // 一分钟以后过期
 
test('到点就调用回调', () => {
  const callback = jest.fn();
  expire(callback);
 
  jest.advanceTimersByTime(59999);
  expect(callback).not.toBeCalled();
 
  jest.advanceTimersByTime(1);
  expect(callback).toBeCalledOnce();
})
复制代码

这段代码通过 jest.advanceTimersByTime 精确模拟了宏任务的运行过程,同步完成了原本需要一分钟才能验证一次的异步流程的测试。

又比如下面的测试脚本用来测试一个名为 catchFromURL 的工具函数,该函数可以从当前的 URL 中获取指定的参数作为返回值返回,同时从 URL 中抹去该参数。

这中需求通过 URL 携带 token 信息的业务场景(如单点登录)中是非常常见的。

test('通过URL获取指定的参数值并抹去之', () => {
  const CURRENT_ORIGIN = document.location.origin;
  const testHref = `${CURRENT_ORIGIN}/list/2/detail?a=123b&b=true#section2`;
  history.replaceState(null, '', testHref);
  expect(catchFromURL('a')).toBe('123b');
  expect(document.location.href).toBe(`${CURRENT_ORIGIN}/list/2/detail?b=true#section2`);
})
复制代码

这段测试代码通过 jsdom 来实现对需要测试的环境的模拟。环境的构造和模拟其实是单元测试中的一个难点,由于 jsdom 本身的一些缺陷(如没有实现 Navigator)使得在测试脚本运行的 node 环境中模拟正确的浏览器环境往往需要用到很多的 Hack 技术,这一点在未来的夜点心中会着重中展开讨论。

点到为止

less is more

测试代码无需关心被测试模块的具体实现,点到为止地测试几种必要的流程场景即可。这一方面可以减少写测试逻辑的时间,一方面可以使得业务逻辑具有更大的实现自由度。

对一个业务模块,测试脚本只需要关心该模块所关联的所有外部性即可:

对于函数模块而言,控制它引用的模块、它的输入和它的副作用,验证它的输出和对副作用的影响
对于组件模块而言,控制它依赖的服务、它依赖的子组件、它的 props和它的事件,验证它的渲染结果和 props 中回调的调用情况,而不应该关心它的 state。
下面的脚本通过 enzyme 组件测试工具测试了一个名为 ValidatableInput 的 React 组件。这个组件在失焦(blur)时会触发 onValidate 回调,并传入 inputValue 参数。

test('失焦时触发 onValidate', () => {
    const onValidate = jest.mock();
    const inputValue = '输入的内容';
    const wrapper = shallow(
      <ValidatableInput
        placeholder={''}
        value={inputValue}
        alert={''}
        onChange={onChange}
        onValidate={onValidate}
      />
    );
 
    wrapper.find('.validatable-input').first().simulate('blur');
    expect(onValidate).toBeCalledWith(inputValue);
  });
复制代码

在上述测试用例中我们的测试逻辑完全基于行为开展,只关心失焦的「动作」和执行回调的「反馈」,没有去断言任何关于组件状态的内容。

这样组件可以根据它的需要自由地实现它的内部逻辑,例如添加通过外部的 Provider 来提供 value 和 onChange 成为受控组件的能力。这些实现的变化都不会影响当前这条测试用例的有效性。

上面就是一些对应该用单元测试测什么的看法,把单测用在它最擅长的地方,才能在紧凑的开发节奏中取得事半功倍的效果。

总结:

感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

 视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方进群即可自行领取。    

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

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

相关文章

Arduino开发

文章目录 资源Arduino IDE 的使用1. 使能编译以及烧录的LOG&#xff1a;2. 下载配置3. 下载 Arduino指令程序下载步骤通过下载器下载通过串口下载 关于Arduino IDE工程生成的二进制文件对比Tools-->burn bootloader 和 ArduinoISP例程 的区别自带例程 资源 Arduino通过串口…

解决MySQL大版本升级导致.Net(C#)程序连接报错问题

数据库版本从MySQL 5.7.21 升级到 MySQL8.0.21 数据升级完成后&#xff0c;直接修改程序的数据库连接配置信息 <connectionStrings> <add name"myConnectionString" connectionString"server192.168.31.200;uidapp;pwdFgTDkn0q!75;databasemail;&q…

【缓存】Spring全家桶中@CacheEvict无效情况共有以下几种

Spring全家桶中CacheEvict无效情况共有以下几种 一、背景介绍二、原因分析三、解决方案 一、背景介绍 SpringBoot中使用Cacheable注解缓存数据&#xff0c;使用CacheEvict注解删除缓存。但是在项目使用过程中&#xff0c;发现使用CacheEvict注解删除缓存无效。 拓展&#xff…

猪八戒、程序员客栈、码市哪个更好用?

最近有很多程序员伙伴在用接单平台线上兼职&#xff0c;问题也来了&#xff1a;到底哪个更好用嘞? 选取了几个问的比较多的&#xff1a;猪八戒、程序员客栈、码市。进行了一下简单的比较。 优点: 猪八戒 第一&#xff0c;猪八戒的名气是毋庸置疑的。无论是它成立至今的时间…

NB-IOT的粮库挡粮门异动监测装置

一种基于NBIOT的粮库挡粮门异动监测装置,包括若干个NBIOT开门监测装置,物联网后台管理系统,NBIOT低功耗广域网络和用户访问终端;各个NBIOT开门监测装置通过NBIOT低功耗广域网络与物联网后台管理系统连接,物联网后台管理系统与用户访问终端连接.NBIOT开门监测装置能够对粮库挡粮…

索引创建的原则

索引的创建是数据库优化中非常重要的一部分&#xff0c;正确创建索引可以大大提高查询效率。以下是一些创建索引时需要考虑的原则&#xff1a; 根据查询频率创建索引&#xff1a; 频繁用于检索的列&#xff1a; 那些频繁用于查询的列或经常出现在 WHERE、JOIN、ORDER BY 和 GR…

企业如何在自媒体平台推广

自媒体是企业宣传品牌的平台之一&#xff0c;在自媒体平台上企业能够较为自由的决定文案内容发布时间&#xff0c;同时后台也会有专门的数据分析帮助企业了解每一次推广效果&#xff0c;成本可控、数据可查、效果可追踪&#xff0c;还能与用户或者潜在用户互动&#xff0c;进行…

放射影像科PACS系统源码

PACS系统是医院影像科室中应用的一种系统&#xff0c;主要用于获取、传输、存档和处理医学影像。它通过各种接口&#xff0c;如模拟、DICOM和网络&#xff0c;以数字化的方式将各种医学影像&#xff0c;如核磁共振、CT扫描、超声波等保存起来&#xff0c;并在需要时能够快速调取…

从用户角度出发,如何优化大数据可视化体验|北京蓝蓝UI设计公司

作者&#xff1a;蓝蓝设计-鹤鹤 大数据已经成为人们探索世界的新工具。但是&#xff0c;对于普通用户而言&#xff0c;大数据往往比较抽象和难以理解&#xff0c;因此&#xff0c;大数据可视化作为一种非常有效的工具工具被广泛应用。然而&#xff0c;在实际应用中&#xff0c…

前端如何不变形的渲染图片大小和图片上的内容

在做前端项目时可能经常会页面图片大小变形或者压缩的情况&#xff0c;一般情况就是height给100%&#xff0c;width给auto就可以了满足大部分使用情况了。有时候需要做一些比较复杂的功能&#xff0c;比如需要在图片上增加锚点&#xff0c;而且图片在适配各种屏幕大小时&#x…

el-table动态增加列、行数据,俩种方法实现按需选择

需求&#xff1a; 表格数据过多的时候&#xff0c;需要实现动态选择数据的功能&#xff0c;有俩种方法可以按需选择&#xff0c;解决了表格动态选择时闪屏数据抖动问题。 注意&#xff0c;这个添加数据是tableData原本就有的&#xff0c;我做的这个操作类似就是折叠选择展示原有…

使用Docker Compose部署Spug并实现内网穿透远程访问

文章目录 前言1. Docker安装Spug2 . 本地访问测试3. Linux 安装cpolar4. 配置Spug公网访问地址5. 公网远程访问Spug管理界面6. 固定Spug公网地址 前言 Spug 面向中小型企业设计的轻量级无 Agent 的自动化运维平台&#xff0c;整合了主机管理、主机批量执行、主机在线终端、文件…

产品经理一定要学会的原型交互规范设计

一、主级按钮 序号1、序号2&#xff1a;主级按钮 一个按钮区最多一个主级按钮&#xff0c;也可以没有 二、线框按钮 序号3&#xff1a;如果不是非常为了突出“完成”、“推荐”的操作&#xff0c;可以多采用线框按钮 三、红色按钮 序号4&#xff1a;红色按钮不需要选中颜色这…

音视频rtsp rtmp gb28181在浏览器上的按需拉流

按需拉流是从客户视角来看待音视频的产品功能&#xff0c;直观&#xff0c;好用&#xff0c;为啥hls flv大行其道也是这个原因&#xff0c;不过上述存在的问题是延迟没法降到实时毫秒级延迟&#xff0c;也不能随心所欲的控制。通过一段时间的努力&#xff0c;结合自己闭环技术栈…

没搞错吧?阿里云99元一年服务器老用户可以买!

阿里云老用户优惠服务器99元/年&#xff0c;谁再说阿里云不好我给谁急&#xff0c;云服务器ECS配置为经济型e实例&#xff0c;2核CPU、2G内存、3M固定带宽、40G ESSD entry 系统盘&#xff0c;老用户优惠价99元一年&#xff0c;老用户可以买&#xff0c;当然新用户也可以买&…

[推荐]SpringBoot,邮件发送附件含Excel文件(含源码)。

在阅读本文前&#xff0c;可以先阅读我的上一篇文章&#xff1a; SpringBoot&#xff0c;使用JavaMailSender发送邮件(含源码)。 &#xff0c;本文使用的代码案例涉及到的 jar包、application.properties配置与它相同。 先看一下效果。 图一 图二 在下方代码案例中&#xff0c;…

【电路笔记】-正弦波形

正弦波 文章目录 正弦波1、概述2、波形产生3、总结 在 19 世纪末的 10 年间&#xff0c;许多技术成就使得交流电的使用得以扩展&#xff0c;并克服了直流电向公众供电的局限性。 1882 年&#xff0c;法国发明了变压器&#xff0c;它简化了交流电的分配&#xff0c;正如我们将在…

数字人小灿:始于火山语音,发于 B 端百业

火爆的数字人市场又有新消息来袭&#xff1a;火山语音的数字人小灿来了&#xff01; 数字人小灿首曝视频 今年以来&#xff0c;在生成式AI浪潮的助推下&#xff0c;大量企业争相布局数字人赛道。市场之所以如此火热&#xff0c;是因为AI数字人已被视为人工智能时代智能交互的入…

gcc/g++使用格式+各种选项,预处理/编译(分析树,编译优化,生成目标代码)/汇编/链接过程(函数库,动态链接)

目录 gcc/g--编译器 介绍 使用格式 通用选项 编译选项 链接选项 程序编译过程 预处理(宏替换) 编译 (生成汇编) 分析树(parse tree) 编译优化 删除死代码 寄存器分配和调度 强度削弱 内联函数 生成目标代码 汇编 (生成二进制代码) 链接(生成可执行文件) 函…

相册里的视频怎么提取音频?帮你整理了几个必备的!

有的时候视频中的音频包含重要信息&#xff0c;如对话、旁白、音乐等。提取音频不仅可以节省存储空间&#xff0c;还方便对这些信息进行单独处理和利用。那么如何提取音频呢&#xff1f;下面介绍了3种方法~ 方法一&#xff1a;直接使用手机相册自带功能 1、打开手机相册&#…