如何用 JavaScript 编写你的第一个单元测试

news2025/2/28 16:59:11

前言

测试代码是使代码安全的第一步。做到这一点的最好方法之一是使用单元测试,确保应用程序中的每个小功能都能发挥其应有的作用–特别是当应用程序处于边缘情况,比如无效的输入,或有潜在危害的输入。

为什么要单元测试

说到单元测试,有许多不同的方法。单元测试的一些主要目的是:

  • 验证功能:单元测试确保代码做正确的事情,不做不应该做的事情–这是大多数错误发生的地方。
  • 防止代码混乱:当我们发现一个bug时,添加一个单元测试来检查这个场景,可以保证代码的更改不会在将来重新引入这个bug。
  • 文档化代码:有了正确的单元测试,一套完整的测试和结果提供了一个应用程序应该如何运行的规范。
  • 代码更安全:单元测试可以检查可被利用的漏洞(比如那些可以实现恶意SQL注入的漏洞)。

确定范围

使用单元测试框架使我们能够快速编写和自动化我们的测试,并将它们集成到我们的开发和部署过程中。这些框架通常支持在前端和后端的JavaScript代码中进行测试。

下面是一些帮助你编写性能单元测试和可测试代码的一般准则。

保持简短

不要让你的单元测试冗余。测试应该只有几行代码,检查应用程序的代码块。

同时考虑正反面

编写一个测试来确认一个函数的正确执行是有帮助的。然而,编写一套更广泛的测试,检查一个函数在被误用时或在边缘情况下是否会失败,会更有效果。这些负面测试甚至更有价值,因为它们有助于预测意外情况。例如一个函数什么时候应该抛出异常,或者它应该如何处理接收到的畸形数据。

分解复杂功能

含有大量逻辑的大型函数很难测试;包括太多的操作,无法有效测试每个变量。如果一个函数过于复杂,可以将其分割成较小的函数进行单独测试。

避免网络和数据库连接

单元测试应该快速且轻量,但是函数会发出网络请求,或者连接其他程序并花很长时间执行。这使得同时运行许多操作具有挑战性,并可能产生更脆弱的代码。你可以在单元测试中造假数据来实现模拟的网络或数据库调用,这可以让你测试函数的其余部分。你可以在不同的测试过程中包含真正的网络和数据库连接,这称为集成测试。

如何编写单元测试

现在,我们已经回顾了一些单元测试的最佳实践,你已经准备好在JavaScript中编写你的第一个单元测试。

本教程使用了Mocha框架,它是最流行的单元测试之一。每个测试框架都略有不同,但足够相似,学习基本概念将使你能够在它们之间切换自如。

要跟着示例,请确保电脑上已经安装了Node.js。

创建新项目

首先,打开终端窗口或命令提示符到一个新的项目文件夹。然后,通过输入npm init -y在其中创建一个新的Node.js项目。

这会在文件夹内创建package.json文件,使你能够使用npm install -D mocha将Mocha安装为开发依赖。

接着,在编辑器中打开package.json文件,用mocha替换占位符测试脚本:

"scripts": {"test": "mocha"
 }, 

实现一个类

接下来,编写一个简单的交通灯系统,进行单元测试。

在项目的目录内,创建traffic.js文件,并为TrafficLight类添加如下代码:

class TrafficLight {constructor() {this.lightIndex = 0;}static get colors() {return [ "green", "yellow", "red" ];}get light() {return TrafficLight.colors[ this.lightIndex ];}next() {this.lightIndex++;// This is intentionally wrong!if( this.lightIndex > TrafficLight.colors.length ) {this.lightIndex = 0;}}
}

module.exports = TrafficLight; 

该类包含了四部分:

  • TrafficLight.colors:交通灯颜色的常量属性。
  • lightIndex:追踪当前交通灯颜色索引变量。
  • light:将当前交通灯颜色作为字符串返回的类的属性。
  • next():更改交通灯为下个颜色的函数。

添加单元测试

是时候为代码添加单元测试了。

在项目的目录下创建名为test的文件夹。这里是Mocha默认检查单元测试的地方。在test文件夹下添加traffic.test.js文件。

接着,在文件顶部导入TrafficLight

const TrafficLight = require( "../traffic" ); 

我们要用到测试的assert模块,因此也需要导入:

const assert = require( "assert" ); 

在Mocha的帮助下,我们可以使用describe()函数将单元测试分组。因此我们可以为这个类设置一个顶级组,如下所示:

describe( "TrafficLight", function () {
}); 

然后,我们在子组中添加校验交通灯颜色的单元测试,位于TrafficLight集合内部,并称为colors

describe( "TrafficLight", function () {describe( "colors", function () {});
}); 

对于第一个单元测试,我们可以检查colors仅有三个状态:绿色、黄色和红色。该测试在describe()组内部,使用it()函数定义。因此可以这样编写测试用例:

describe( "TrafficLight", function () {describe( "colors", function () {it( "has 3 states", function () {const traffic = new TrafficLight();assert.equal( 3, TrafficLight.colors.length );});});
}); 

现在,让我们试着运行单元测试,看看是否可以通过。

在终端窗口中运行npm test,如果一切正常,Mocha会打印出单元测试运行的结果。

添加更多单元测试

我们的项目现在已经准备好运行单元测试了,因此可以添加更多的单元测试,确保代码正确运行。

首先,添加一个单元测试到colors组,验证交通信号灯的颜色是否正确,是否符合顺序。下面是实现测试的一种方式:

it( "colors are in order", function () {const expectedLightOrder = [ "green", "yellow", "red" ];const traffic = new TrafficLight();for( let i = 0; i < expectedLightOrder.length; i++ ) {assert.equal( expectedLightOrder[ i ], TrafficLight.colors[ i ] );}
}); 

其次,测试next()函数,看看信号灯是否可以正确切换。创建一个新的子组,并添加两个单元测试:一个用来检查灯是否按顺序正确切换,另一个用来检查在循环到红色后是否返回到绿色。

按照如下方式编写单元测试:

describe( "next()", function () {it( "changes lights in order", function () {const traffic = new TrafficLight();for( let i = 0; i < TrafficLight.colors.length; i++ ) assert.equal( traffic.light, TrafficLight.colors[ i ] );traffic.next();}});it( "loops back to green", function () {const traffic = new TrafficLight();// Change the light 3x to go from green -> yellow -> red -> greenfor( let i = 0; i < 3; i++ ) {traffic.next();}assert.equal( traffic.light, TrafficLight.colors[ 0 ] );});
}); 

现在,当我们重新运行测试时,我们会看到其中一个测试失败了。这是因为TrafficLight类中有一个错误。

修复bug

再次打开TrafficLight类的代码,找到next()函数内的注释,其内容为// This is intentionally wrong!

从我们的单元测试中,我们知道这个函数没有正确地返回到绿色。我们可以看到,目前的代码在lightIndex值超过交通灯颜色的数量时进行检查,但索引是从0开始的。相反,我们必须在该索引值达到颜色数量时返回到绿色。让我们更新代码,当lightIndex值等于交通灯颜色列表的长度时,将其重置为0

// This is intentionally wrong!
if( this.lightIndex === TrafficLight.colors.length ) {this.lightIndex = 0;
} 

现在你所有的单元测试都应该通过。最重要的是,即使TrafficLight类被重构或大量修改,我们的单元测试也会在它触达用户之前捕获这个错误。

总结

单元测试很容易设置,是软件开发的有效工具。它们有助于早期消除错误,并防止它们返回。这使项目更易于管理和维护,即使它们变得更大和更复杂,特别是在更大的开发团队中。像这样的自动化测试也使开发人员能够重构和优化他们的代码,而不必担心新代码的行为是否正确。

单元测试是开发流程中的一个关键部分,对于帮助你构建更好、更安全的JavaScript应用至关重要。

祝你测试愉快!

以上就是本文的所有内容,如果对你有所帮助,欢迎收藏、点赞、转发~

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

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

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

相关文章

测试开发之Django实战示例 第十章 创建在线教育平台

第十章 创建在线教育平台在上一章&#xff0c;我们为电商网站项目添加了国际化功能&#xff0c;还创建了优惠码和商品推荐系统。在本章&#xff0c;会建立一个新的项目&#xff1a;一个在线教育平台&#xff0c;并创内容管理系统CMS&#xff08;Content Management System&…

OSI模型详解

今天&#xff0c;我们详解OSI&#xff08;Open System Inter-connection Reference Model&#xff09;模型&#xff0c;来看看工业物联网的网络互联和数据互通。 OSI模型 1984年&#xff0c;国际标准化组织&#xff08;International Organization for Standardization&#…

C控制语句(continue,break)

一.continue 3种循环都可以使用continue语句。执行到该语句时&#xff0c;会跳过本次迭代的剩余部分&#xff0c;并开始下一轮迭代 for(count0;count<10;count) { ch getchar(); If(ch \n) continue; putchar(ch&#xff09;&#xff1b; } 这条代码当中当ch等于\…

MySQL CAST()函数用法

一、语法 expr&#xff1a;源数据&#xff0c;如字符串’China’。type&#xff1a;目标数据类型&#xff0c;例如CHAR。 cast(expr AS type)二、命令说明 将任何类型的值转换为具有指定类型的值。 CAST()函数通常用于返回具有指定类型的值&#xff0c;以便在WHERE&#xff…

Vue3+mitt.js配置logger

前言 Vue项目中的组件通信方式&#xff0c;绝大多数的情况是可以被Vuex等方案代替的&#xff0c;但有一些特殊情况却非常适合使用EventBus&#xff0c;举个简单的例子&#xff1a;有A、B两个组件&#xff0c;用户在A上进行操作后&#xff0c;需要B执行某些逻辑。 由于Vue3中删…

TiDB在转转公司的发展历程

1 前言 2 运维痛点 3 解决痛点 3.1 元数据管理 3.2 机器资源管理 3.3 全面升级 3.4 告警改造 4 实现自动化 4.1 需求工单化 4.2 操作平台化 4.3 其他辅助功能 5 写在最后 1 前言 转转是PingCAP最早的一批用户之一&#xff0c;见证了TiDB的发展&#xff0c;自身也沉淀…

计讯物联高精度定位GNSS接收机TN521在水库大坝变形监测的应用解析

由于水库大坝在地质环境恶劣和气候条件复杂的条件下&#xff0c;水库坝体的稳定性会受到严重考验。为了保证水库大坝的安全运营&#xff0c;GNSS作为一种实现远程自动化测量的高精度的变形监测技术&#xff0c;具有高精度、高速度、全天候、连续实时、自动化等优势&#xff0c;…

WMS系统推荐,如何选到适合企业的仓库管理系统

市场上有很多WMS系统&#xff0c;但是现在很多仓库管理系统都在使用WMS系统。那么在选择WMS系统时应该考虑什么呢&#xff1f;明确业务发展特征&#xff0c;准确表达能力目标许多物流企业在选择物流管理系统时&#xff0c;往往会被物流管理系统的整体系统所迷惑&#xff0c;在功…

MySql的sql语句执行过程详述

目录 前言&#xff1a; sql语句的执行过程&#xff1a; server层&#xff1a; 存储引擎&#xff1a; 连接器&#xff1a; 查询缓存&#xff1a; 分析器&#xff1a; 优化器&#xff1a; 执行器&#xff1a; 前言&#xff1a; 很多人都在使用mysql数据库&#xff0c;但…

【软件测试】测试人的bug一生,资深测试的专业角度......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 对于测试人员来说&a…

【Linux】进程的描述组织与进程状态

文章目录&#x1f3aa; 进程的描述组织&#x1f680;1.什么是进程&#x1f680;2.进程的形成&#x1f680;3.进程标识符 *⭐3.1 PS命令查看PID⭐3.2 /proc目录查看进程属性&#x1f680;4.父子进程⭐4.1 系统调用获取PID⭐4.2 fork创建子进程⭐4.3 fork双返回值问题⭐4.4 写时拷…

Netty源码解读-EventLoop(二)

一、简介 NioEventLoop的重要组成&#xff1a;Selector、线程、任务队列&#xff0c;他既会处理io事件&#xff0c;也会处理普通任务和定时任务. 1.下面是Selector&#xff0c;注意有两个哦后面会讲 2.下面的爷爷类提供的Thread变量&#xff0c;其实下面发excutor用的就是这个…

vue2中defineProperty和vue3中proxy区别

区别一&#xff1a;defineProperty 是对属性劫持&#xff0c;proxy 是对代理对象 下面我们针对一个对象使用不同的方式进行监听&#xff0c;看写法上有什么不同。 // 原始对象 const data {name: Jane,age: 21 }defineProperty defineProperty 只能劫持对象的某一个属性&…

TCP核心机制详解(三)

目录 前言&#xff1a; 滑动窗口 滑动窗口处理丢包问题 流量控制 拥塞控制 延时应答 捎带应答 面向字节流 异常情况 小结&#xff1a; 前言&#xff1a; 前两篇文章讲述了&#xff0c;TCP十种核心机制的前三种。这篇文章详细介绍其他的一些核心机制&#xff0c;让我们…

大气温室气体浓度不断增加,导致气候变暖加剧,随之会引发一系列气象、生态和环境灾害怎样解决?

大气温室气体浓度不断增加&#xff0c;导致气候变暖加剧&#xff0c;随之会引发一系列气象、生态和环境灾害。如何降低温室气体浓度和应对气候变化已成为全球关注的焦点。海洋是地球上最大的“碳库”,“蓝碳”即海洋活动以及海洋生物&#xff08;特别是红树林、盐沼和海草&…

企企通持续助力全球管道预制先行者「迈科管道」,二期项目逐步启动

近日&#xff0c;国内管道预制龙头企业「济南迈科管道科技有限公司」签约企企通采购供应链系统二期项目。此次签约&#xff0c;将针对迈科管道的采购业务特点和需求&#xff0c;结合企企通采购与供应链管理系统优势和丰富的实战经验&#xff0c;在一期项目基础上&#xff0c;持…

山东科技大学校历 代码分析 获得以前学期学年的老版校历

校历-山东科技大学网络安全与信息化办公室http://tech.sdust.edu.cn/wlfw/xl.htm JavaScript var studyStart new Date(2021, 8-1, 30);//8-1 -1没有意义&#xff0c;30代表30号&#xff0c;2021年8月30号开始这个学期 var commonWeeks 19;//这个学期有多少周 var s…

Mysql使用规范(纯技术和实战建议)

1、事务隔级别: &#xff08;强制&#xff09;&#xff1a;Repeatable-Read&#xff08;重复读&#xff09;&#xff0c;且不能在会话操作时临时开启隔离级别。 注&#xff1a; Repeatable-Read&#xff08;重复读&#xff09;隔离级别解决不了幻读。 可用 show variables l…

Linux学习之环境变量

目录环境变量基本概念查看环境变量的方法常见的环境变量PATH&#xff1a;指定命令的搜索路径。PATH测试HOME&#xff1a;指定用户的主工作目录SHELL&#xff1a;当前Shell和环境变量有关的命令环境变量的组织方式通过代码来获得环境变量通过函数来获得环境变量环境变量具有全局…