51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 543|回复: 0
打印 上一主题 下一主题

[原创] JavaScript编写单元测试看这里

[复制链接]
  • TA的每日心情
    无聊
    7 小时前
  • 签到天数: 938 天

    连续签到: 5 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2023-2-14 10:50:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
     前言
      测试代码是使代码安全的第一步。做到这一点的最好方法之一是使用单元测试,确保应用程序中的每个小功能都能发挥其应有的作用--特别是当应用程序处于边缘情况,比如无效的输入,或有潜在危害的输入。
      为什么要单元测试
      说到单元测试,有许多不同的方法。单元测试的一些主要目的是:
      验证功能:单元测试确保代码做正确的事情,不做不应该做的事情--这是大多数错误发生的地方。
      防止代码混乱:当我们发现一个bug时,添加一个单元测试来检查这个场景,可以保证代码的更改不会在将来重新引入这个bug。
      文档化代码:有了正确的单元测试,一套完整的测试和结果提供了一个应用程序应该如何运行的规范。
      代码更安全:单元测试可以检查可被利用的漏洞(比如那些可以实现恶意SQL注入的漏洞)。
      确定范围
      使用单元测试框架使我们能够快速编写和自动化我们的测试,并将它们集成到我们的开发和部署过程中。这些框架通常支持在前端和后端的JavaScript代码中进行测试。
      下面是一些帮助你编写性能单元测试和可测试代码的一般准则。
      保持简短
      不要让你的单元测试冗余。测试应该只有几行代码,检查应用程序的代码块。
      同时考虑正反面
      编写一个测试来确认一个函数的正确执行是有帮助的。然而,编写一套更广泛的测试,检查一个函数在被误用时或在边缘情况下是否会失败,会更有效果。这些负面测试甚至更有价值,因为它们有助于预测意外情况。例如一个函数什么时候应该抛出异常,或者它应该如何处理接收到的畸形数据。
      分解复杂功能
      含有大量逻辑的大型函数很难测试;包括太多的操作,无法有效测试每个变量。如果一个函数过于复杂,可以将其分割成较小的函数进行单独测试。
      避免网络和数据库连接
      单元测试应该快速且轻量,但是函数会发出网络请求,或者连接其他程序并花很长时间执行。这使得同时运行许多操作具有挑战性,并可能产生更脆弱的代码。你可以在单元测试中造假数据来实现模拟的网络或数据库调用,这可以让你测试函数的其余部分。你可以在不同的测试过程中包含真正的网络和数据库连接,这称为集成测试。
      如何编写单元测试
      现在,我们已经回顾了一些单元测试的最佳实践,你已经准备好在JavaScript中编写你的第一个单元测试。
      本教程使用了Mocha框架,它是最流行的单元测试之一。每个测试框架都略有不同,但足够相似,学习基本概念将使你能够在它们之间切换自如。
      要跟着示例,请确保电脑上已经安装了Node.js。
      创建新项目
      首先,打开终端窗口或命令提示符到一个新的项目文件夹。然后,通过输入npm init -y在其中创建一个新的Node.js项目。
      这会在文件夹内创建package.json文件,使你能够使用npm install -D mocha将Mocha安装为开发依赖。
      接着,在编辑器中打开package.json文件,用mocha替换占位符测试脚本:
    1. "scripts": {
    2.       "test": "mocha"
    3.    },
    复制代码



    实现一个类
      接下来,编写一个简单的交通灯系统,进行单元测试。
      在项目的目录内,创建traffic.js文件,并为TrafficLight类添加如下代码:

    1. class TrafficLight {
    2.       constructor() {
    3.           this.lightIndex = 0;
    4.       }
    5.       static get colors() {
    6.           return [ "green", "yellow", "red" ];
    7.       }
    8.       get light() {
    9.           return TrafficLight.colors[ this.lightIndex ];
    10.       }
    11.       next() {
    12.           this.lightIndex++;
    13.           // This is intentionally wrong!
    14.           if( this.lightIndex > TrafficLight.colors.length ) {
    15.               this.lightIndex = 0;
    16.           }
    17.       }
    18.   }
    19.   module.exports = TrafficLight;
    复制代码


    该类包含了四部分:
      ·TrafficLight.colors:交通灯颜色的常量属性。
      · lightIndex:追踪当前交通灯颜色索引变量。
      · light:将当前交通灯颜色作为字符串返回的类的属性。
      · next():更改交通灯为下个颜色的函数。
      添加单元测试
      是时候为代码添加单元测试了。
      在项目的目录下创建名为test的文件夹。这里是Mocha默认检查单元测试的地方。在test文件夹下添加traffic.test.js文件。
      接着,在文件顶部导入TrafficLight:
    1.   const TrafficLight = require( "../traffic" );
    复制代码


    我们要用到测试的assert模块,因此也需要导入:
    1.   const assert = require( "assert" );
    复制代码


    在Mocha的帮助下,我们可以使用describe()函数将单元测试分组。因此我们可以为这个类设置一个顶级组,如下所示:
    1. describe( "TrafficLight", function () {
    2.   });
    复制代码


    然后,我们在子组中添加校验交通灯颜色的单元测试,位于TrafficLight集合内部,并称为colors:
    1. describe( "TrafficLight", function () {
    2.       describe( "colors", function () {
    3.       });
    4.   });
    复制代码


    对于第一个单元测试,我们可以检查colors仅有三个状态:绿色、黄色和红色。该测试在describe()组内部,使用it()函数定义。因此可以这样编写测试用例
    1. describe( "TrafficLight", function () {
    2.       describe( "colors", function () {
    3.           it( "has 3 states", function () {
    4.               const traffic = new TrafficLight();
    5.               assert.equal( 3, TrafficLight.colors.length );
    6.           });
    7.       });
    8.   });
    复制代码


    现在,让我们试着运行单元测试,看看是否可以通过。
      在终端窗口中运行npm test,如果一切正常,Mocha会打印出单元测试运行的结果。

      添加更多单元测试
      我们的项目现在已经准备好运行单元测试了,因此可以添加更多的单元测试,确保代码正确运行。
      首先,添加一个单元测试到colors组,验证交通信号灯的颜色是否正确,是否符合顺序。下面是实现测试的一种方式:
    1.  it( "colors are in order", function () {
    2.       const expectedLightOrder = [ "green", "yellow", "red" ];
    3.       const traffic = new TrafficLight();
    4.       for( let i = 0; i < expectedLightOrder.length; i++ ) {
    5.           assert.equal( expectedLightOrder[ i ], TrafficLight.colors[ i ] );
    6.       }
    7.   });
    复制代码


    其次,测试next()函数,看看信号灯是否可以正确切换。创建一个新的子组,并添加两个单元测试:一个用来检查灯是否按顺序正确切换,另一个用来检查在循环到红色后是否返回到绿色。
      按照如下方式编写单元测试:
    1. describe( "next()", function () {
    2.       it( "changes lights in order", function () {
    3.           const traffic = new TrafficLight();
    4.           for( let i = 0; i < TrafficLight.colors.length; i++ )
    5.               assert.equal( traffic.light, TrafficLight.colors[ i ] );
    6.               traffic.next();
    7.           }
    8.       });
    9.       it( "loops back to green", function () {
    10.           const traffic = new TrafficLight();
    11.           // Change the light 3x to go from green -> yellow -> red -> green
    12.           for( let i = 0; i < 3; i++ ) {
    13.               traffic.next();
    14.           }
    15.           assert.equal( traffic.light, TrafficLight.colors[ 0 ] );
    16.       });
    17.   });
    复制代码


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

      修复bug
      再次打开TrafficLight类的代码,找到next()函数内的注释,其内容为// This is intentionally wrong!。
      从我们的单元测试中,我们知道这个函数没有正确地返回到绿色。我们可以看到,目前的代码在lightIndex值超过交通灯颜色的数量时进行检查,但索引是从0开始的。相反,我们必须在该索引值达到颜色数量时返回到绿色。让我们更新代码,当lightIndex值等于交通灯颜色列表的长度时,将其重置为0:
    1. // This is intentionally wrong!
    2.   if( this.lightIndex === TrafficLight.colors.length ) {
    3.       this.lightIndex = 0;
    4.   }
    复制代码


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

      总结
      单元测试很容易设置,是软件开发的有效工具。它们有助于早期消除错误,并防止它们返回。这使项目更易于管理和维护,即使它们变得更大和更复杂,特别是在更大的开发团队中。像这样的自动化测试也使开发人员能够重构和优化他们的代码,而不必担心新代码的行为是否正确。
      单元测试是开发流程中的一个关键部分,对于帮助你构建更好、更安全的JavaScript应用至关重要。





    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-4-26 16:37 , Processed in 0.059387 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表