如何用 JavaScript 编写你的第一个单元测试
前言
测试代码是使代码安全的第一步。做到这一点的最好方法之一是使用单元测试,确保应用程序中的每个小功能都能发挥其应有的作用--特别是当应用程序处于边缘情况,比如无效的输入,或有潜在危害的输入。
为什么要单元测试
说到单元测试,有许多不同的方法。单元测试的一些主要目的是:
- 验证功能:单元测试确保代码做正确的事情,不做不应该做的事情--这是大多数错误发生的地方。
- 防止代码混乱:当我们发现一个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 -> green
for( 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应用至关重要。
祝你测试愉快!
以上就是本文的所有内容,如果对你有所帮助,欢迎收藏、点赞、转发~
如何用 JavaScript 编写你的第一个单元测试的更多相关文章
- 编程原理—如何用javascript代码解决一些问题
关于编程,我最喜欢的就是解决问题.我不相信有谁天生具有解决问题的能力.这是一种通过反复锻炼而建立并维持的能力.像任何练习一样,有一套指导方针可以帮助你更有效地提高解决问题的能力.我将介绍5个最重要的软 ...
- javascript 编写的贪吃蛇
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- javascript编写一个简单的编译器(理解抽象语法树AST)
javascript编写一个简单的编译器(理解抽象语法树AST) 编译器 是一种接收一段代码,然后把它转成一些其他一种机制.我们现在来做一个在一张纸上画出一条线,那么我们画出一条线需要定义的条件如下: ...
- JavaScript学习总结(十二)——JavaScript编写类
在工作中经常用到JavaScript,今天总结一下JavaScript编写类的几种写法以及这几种写法的优缺点,关于JavaScript编写类的方式,在网上看到很多,而且每个人的写法都不太一样,经常看到 ...
- 【教程】HTML5+JavaScript编写flappy bird
作者: 风小锐 新浪微博ID:永远de风小锐 QQ:547953539 转载请注明出处 PS:新修复了两个bug,已下载代码的同学请查看一下 大学立即要毕业了. ...
- 一步一步学Silverlight 2系列(22):在Silverlight中如何用JavaScript调用.NET代码
概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, ...
- 深入理解javascript选择器API系列第一篇——4种元素选择器
× 目录 [1]id属性 [2]标签名 [3]name属性[4]all 前面的话 说到最常见的DOM应用,恐怕就要数取得特定的某个或某组元素的引用了.DOM定义了许多方式来选取元素,包括getElem ...
- artDialog是一个基于javascript编写的对话框组件,它拥有精致的界面与友好的接口
artDialog是一个基于javascript编写的对话框组件,它拥有精致的界面与友好的接口 自适应内容 artDialog的特殊UI框架能够适应内容变化,甚至连外部程序动态插入的内容它仍然能自适应 ...
- 用Javascript编写Chrome浏览器插件
原文:http://homepage.yesky.com/62/11206062.shtml 用Javascript编写Chrome浏览器插件 2010-04-12 07:30 来源:天极网软件频道 ...
- 如何用Java编写一段代码引发内存泄露
本文来自StackOverflow问答网站的一个热门讨论:如何用Java编写一段会发生内存泄露的代码. Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码.这个问题我一点思路都没有, ...
随机推荐
- Prometheus使用nginx 设置二级路径反向代理
1.nginx 设置 location /promethues/ { proxy_pass http://10.xx.xxx.55:9090/prometheus/; } 2.设置prometheus ...
- kvm使用桥接的方法
什么是桥接 桥接就是把物理机的网卡模拟成交换机,虚拟机的网卡直接连在虚拟的网桥即交换机上.这样kvm虚拟机分配的IP地址,就应该和物理机在同一网段,可以对外进行服务. 在KVM下运行的VM默认的网卡采 ...
- Oracle基础知识汇总一
Oracle基础知识 以下内容为本人的学习笔记,如需要转载,请声明原文链接 https://www.cnblogs.com/lyh1024/p/16720759.html oracle工具: SQ ...
- .Net 7 C#11 原始字符串
.Net7 的到来的同时,也带来了 C# 11,而令我最期待的就是 C# 11 的 原始字符串了,当我知道这个的时候,简直比过年还要开心. 非原始字符串 首先我们看看现在写字符串的方式 var str ...
- k3s部署全过程
# 安装k3s博客 ## 准备工作 1.准备俩台可以相互访问的服务器 2.需要先安装dockers 3.以下教程将使用VsCode+ssh插件来进行插件图 ssh连接到俩台服务器 点击打开ssh操作界 ...
- 工业互联网领域的企业,都已经接入了ERP或者MES系统了吗?
肯定不是得啊!之前的两化,后来的企业上云,到当下的智能制造.数字化转型,不都是想把制造业(也就是你说的工业互联网企业)往这个方向推么,ERP和MES是企业数字化的一部分,但不是全部,当然有的企业(小工 ...
- 华为交换机STP常用命令
STP配置和选路规则 stp enable 在交换机上启用STP stp mode stp dis stp 查看stp配置 dis stp brief 查看接口摘要信息 stp priority 40 ...
- JUC(4)Callable和常用的辅助类
1.Callable 1.可以有返回值 2.可以抛出异常 3.方法不同.run()/call() future Task 细节: 1.有缓存 2.结果可能需要等待,会阻塞 2.常用的辅助类 2.1 C ...
- 分享个好东西 - 两行前端代码搞定bilibili链接转视频
只需要在您的要解析B站视频的页面的</body>前面加上下面两行代码即可,脚本会在客户端浏览器里解析container所匹配到的容器里的B站超链接 (如果不是外围有a标签的超链接只是纯粹的 ...
- django 生产环境部署手册
Django 是 python 的 web 框架,以下是其部署到生产环境的详细步骤,包含 Apache 和 nginx 版本. 部署环境 操作系统:centeros7.3 数据库:MySQL5.6.5 ...