如何为前端编写单元测试?从这篇入门指南开始学习!

这篇具有很好参考价值的文章主要介绍了如何为前端编写单元测试?从这篇入门指南开始学习!。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

对于现在的前端工程,一个标准完整的项目,通常情况单元测试是非常必要的。但很多时候我们只是完成了项目而忽略了项目测试。我认为其中一个很大的原因是很多人对单元测试认知不够,因此我写了这边文章,一方面期望通过这篇文章让你对单元测试有一个初步认识。另一个方面希望通过代码示例,让你掌握写单元测试实践能力。

前端为什么需要单元测试?

必要性:JavaScript 缺少类型检查,编译期间无法定位到错误,单元测试可以帮助你测试多种异常情况。

正确性:测试可以验证代码的正确性,在上线前做到心里有底。

自动化:通过 console 虽然可以打印出内部信息,但是这是一次性的事情,下次测试还需要从头来过,效率不能得到保证。通过编写测试用例,可以做到一次编写,多次运行。

保证重构:互联网行业产品迭代速度很快,迭代后必然存在代码重构的过程,那怎么才能保证重构后代码的质量呢?有测试用例做后盾,就可以大胆的进行重构。

现状

下面是一份抽样调查片段,抽样依据如下:

向 200 名相关者发出在线问卷调查,其中 70 人回答了问卷中的问题,前端人数占 81.16%,如果你有兴趣的话,也可以帮我填一下调查问卷 (https://www.wjx.cn/vm/Ombu9q1.aspx)

数据收集日期:2021.09.21—2021.10.08

目标群体:所有开发人员

组织规模:不到 50 人,50 到 100人, 100人以上

你执行过 JavaScript 单元测试吗?

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

调查中的另一个有趣的见解是,在大型组织中单元测试更受欢迎。其中一个原因可能是,由于大型组织需要处理大规模的产品,以及频繁的功能迭代吧。这种持续的迭代方式,迫使他们进行自动化测试的投入。更具体地说,单元测试有助于增强产品的整体质量。

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

另外,报告显示超 80% 人认为单元测试可以有效的提高质量,超 60% 人使用过 Jest 去编写前端单元测试,超 40% 的人认为单元测试覆盖率是重要的且覆盖率应该大于 80%。

常见单元测试工具

目前用的最多的前端单元测试框架主要有 Mocha (https://mochajs.cn/)、Jest (https://www.jestjs.cn/),但我推荐你使用 Jest,因为 Jest 和 Mocha 相比,无论从 github starts & issues 量,npm下载量相比,都有明显优势。

github stars 以及 npm 下载量的实时数据,参见:jest vs mocha (https://www.npmtrends.com/jest-vs-mocha) 截图日期为 2021.11.25

Github stars & issues

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

npm 下载量

Jest 的下载量较大,一部分原因是因为 create-react-app 脚手架默认内置了 Jest, 而大部分 react 项目都是用它生成的。

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

从 github starts & issues 以及 npm 下载量角度来看,Jest 的关注度更高,社区也更活跃

框架对比

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

Mocha 生态好,但是需要较多的配置来实现高扩展性

Jest 开箱即用

比如对 sum 函数写用例

./sum.js

function sum(a, b) {
  return a + b;
}

module.exports = sum;

Mocha + Chai 方式

Mocha 需要引入 chai 或则其他断言库去断言, 如果你需要查看覆盖率报告你还需要安装 nyc 或者其他覆盖率工具

./test/sum.test.js

const { expect, assert } = require('chai');
const sum = require('../sum');

describe('sum', function() {
  it('adds 1 + 2 to equal 3', () => {
    assert(sum(1, 2) === 3);
  });
});

Jest 方式

Jest 默认支持断言,同时默认支持覆盖率测试

./test/sum.test.js

const sum = require('./sum');

describe('sum function test', () => {
  it('sum(1, 2) === 3', () => {
    expect(sum(1, 2)).toBe(3);
  });
  
  // 这里 test 和 it 没有明显区别,it 是指: it should xxx, test 是指 test xxx
  test('sum(1, 2) === 3', () => {
    expect(sum(1, 2)).toBe(3);
  });
})

可见无论是受欢迎度和写法上,Jest 都有很大的优势,因此推荐你使用开箱即用的 Jest

如何开始?

1.安装依赖

npm install --save-dev jest

2.简单的例子

首先,创建一个 sum.js 文件

./sum.js

function sum(a, b) {
  return a + b;
}

module.exports = sum;

创建一个名为 sum.test.js 的文件,这个文件包含了实际测试内容:

./test/sum.test.js

const sum = require('../sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

将下面的配置部分添加到你的 package.json 里面

{
  "scripts": {
    "test": "jest"
  },
}

运行 npm run test ,jest 将打印下面这个消息

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

3.不支持部分 ES6 语法

nodejs 采用的是 CommonJS 的模块化规范,使用 require 引入模块;而 import 是 ES6 的模块化规范关键字。想要使用 import,必须引入 babel 转义支持,通过 babel 进行编译,使其变成 node 的模块化代码

如以下文件改写成 ES6 写法后,运行 npm run test将会报错

./sum.js

export function sum(a, b) {
  return a + b;
}

./test/sum.test.js

import { sum } from '../sum';

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

为了能使用这些新特性,我们就需要使用 babel 把 ES6 转成 ES5 语法

解决办法

安装依赖

npm install --save-dev @babel/core @babel/preset-env

根目录加入.babelrc

{   "presets": ["@babel/preset-env"] }

再次运行 npm run test ,问题解决

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

原理

jest 运行时内部先执行( jest-babel ),检测是否安装 babel-core,然后取 .babelrc 中的配置运行测试之前结合 babel 先把测试用例代码转换一遍然后再进行测试

4.测试 ts 文件

jest 需要借助 .babelrc 去解析 TypeScript 文件再进行测试

安装依赖

npm install --save-dev @babel/preset-typescript

**改写 **.babelrc

{   "presets": ["@babel/preset-env", "@babel/preset-typescript"] }

为了解决编辑器对 jest 断言方法的类型报错,如 test、expect 的报错,你还需要安装

npm install --save-dev @types/jest

./get.ts

/**
 * 访问嵌套对象,避免代码中出现类似 user && user.personalInfo ? user.personalInfo.name : null 的代码
 */
export function get<T>(object: any, path: Array<number | string>, defaultValue?: T) : T {
  const result = path.reduce((obj, key) => obj !== undefined ? obj[key] : undefined, object);

  return result !== undefined ? result : defaultValue;
}

./test/get.test.ts

import { get } from './get';

test('测试嵌套对象存在的可枚举属性 line1', () => {
  expect(get({
    id: 101,
    email: 'jack@dev.com',
    personalInfo: {
      name: 'Jack',
      address: {
        line1: 'westwish st',
        line2: 'washmasher',
        city: 'wallas',
        state: 'WX'
      }
    }
  }, ['personalInfo', 'address', 'line1'])).toBe('westwish st');
});
现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:1150305204【暗号:csdn000】

 如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

 

 

运行 npm run test

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

5.持续监听

为了提高效率,可以通过加启动参数的方式让 jest 持续监听文件的修改,而不需要每次修改完再重新执行测试用例

改写 package.json

"scripts": {     "test": "jest --watchAll"   },

效果

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

5.生成测试覆盖率报告

什么是单元测试覆盖率?

单元测试覆盖率是一种软件测试的度量指标,指在所有功能代码中,完成了单元测试的代码所占的比例。有很多自动化测试框架工具可以提供这一统计数据,其中最基础的计算方式为:

单元测试覆盖率 = 被测代码行数 / 参测代码总行数 * 100%

如何生成?

加入 jest.config.js 文件

module.exports = {
  // 是否显示覆盖率报告
  collectCoverage: true,
  // 告诉 jest 哪些文件需要经过单元测试测试
  collectCoverageFrom: ['get.ts', 'sum.ts', 'src/utils/**/*'],
}

再次运行效果

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

参数解读
如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

设置单元测试覆盖率阀值

个人认为既然在项目中集成了单元测试,那么非常有必要关注单元测试的质量,而覆盖率则一定程度上客观的反映了单测的质量,同时我们还可以通过设置单元测试阀值的方式提示用户是否达到了预期质量。

jest.config.js 文件

module.exports = {
  collectCoverage: true, // 是否显示覆盖率报告
  collectCoverageFrom: ['get.ts', 'sum.ts', 'src/utils/**/*'], // 告诉 jest 哪些文件需要经过单元测试测试
  coverageThreshold: {
    global: {
      statements: 90, // 保证每个语句都执行了
      functions: 90, // 保证每个函数都调用了
      branches: 90, // 保证每个 if 等分支代码都执行了
    },
  },

上述阀值要求我们的测试用例足够充分,如果我们的用例没有足够充分,则下面的报错将会帮助你去完善

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

6.如何编写单元测试

下面我们以 fetchEnv 方法作为案例,编写一套完整的单元测试用例供读者参考

编写 fetchEnv 方法

./src/utils/fetchEnv.ts 文件

/**
 * 环境参数枚举
 */
 enum IEnvEnum {
  DEV = 'dev', // 开发
  TEST = 'test', // 测试
  PRE = 'pre', // 预发
  PROD = 'prod', // 生产
}

/**
 * 根据链接获取当前环境参数
 * @param {string?} url 资源链接
 * @returns {IEnvEnum} 环境参数
 */
export function fetchEnv(url: string): IEnvEnum {
  const envs = [IEnvEnum.DEV, IEnvEnum.TEST, IEnvEnum.PRE];

  return envs.find((env) => url.includes(env)) || IEnvEnum.PROD;
}

编写对应的单元测试

./test/fetchEnv.test.ts 文件

import { fetchEnv } from '../src/utils/fetchEnv';

describe('fetchEnv', () => {
  it ('判断是否 dev 环境', () => {
    expect(fetchEnv('https://www.imooc.dev.com/')).toBe('dev');
  });

  it ('判断是否 test 环境', () => {
    expect(fetchEnv('https://www.imooc.test.com/')).toBe('test');
  });

  it ('判断是否 pre 环境', () => {
    expect(fetchEnv('https://www.imooc.pre.com/')).toBe('pre');
  });

  it ('判断是否 prod 环境', () => {
    expect(fetchEnv('https://www.imooc.prod.com/')).toBe('prod');
  });

  it ('判断是否 prod 环境', () => {
    expect(fetchEnv('https://www.imooc.com/')).toBe('prod');
  });
});

执行结果

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

7.常用断言方法

关于断言方法有很多,这里仅摘出常用方法,如果你想了解更多,你可以去 Jest 官网 API (https://www.jestjs.cn/docs/expect) 部分查看

.not 修饰符允许你测试结果不等于某个值的情况

./test/sum.test.js

import { sum } from './sum';

test('sum(2, 4) 不等于 5', () => {
  expect(sum(2, 4)).not.toBe(5);
})

./src/utils/userInfo.js

export const getUserInfo = () => {
  return {
    name: 'moji',
    age: 24,
  }
}

./test/userInfo.test.js

import { getUserInfo }  from '../src/userInfo.js';

test('getUserInfo()返回的对象深度相等', () => {
  expect(getUserInfo()).toEqual(getUserInfo());
})

test('getUserInfo()返回的对象内存地址不同', () => {
  expect(getUserInfo()).not.toBe(getUserInfo());
})

.toHaveLength 可以很方便的用来测试字符串和数组类型的长度是否满足预期

./src/utils/getIntArray.js

export const getIntArray = (num) => {
  if (!Number.isInteger(num)) {
    throw Error('"getIntArray"只接受整数类型的参数');
  }
  
  return [...new Array(num).keys()];
};

./test/getIntArray.test.js

./test/getIntArray.test.js
import { getIntArray }  from '../src/utils/getIntArray';

test('getIntArray(3)返回的数组长度应该为3', () => {
  expect(getIntArray(3)).toHaveLength(3);
})

.toThorw 能够让我们测试被测试方法是否按照预期抛出异常

但是需要注意的是:我们必须使用一个函数将被测试的函数做一个包装,正如下面 getIntArrayWrapFn 所做的那样,否则会因为函数抛出错误导致该断言失败。

./test/getIntArray.test.js

import { getIntArray }  from '../src/utils/getIntArray';

test('getIntArray(3.3)应该抛出错误', () => {
  function getIntArrayWrapFn() {
    getIntArray(3.3);
  }
  
  expect(getIntArrayWrapFn).toThrow('"getIntArray"只接受整数类型的参数');
})

.toMatch 传入一个正则表达式,它允许我们来进行字符串类型的正则匹配

./test/userInfo.test.js

import { getUserInfo }  from '../src/utils/userInfo.js';

test("getUserInfo().name 应该包含'mo'", () => {
  expect(getUserInfo().name).toMatch(/mo/i);
})

测试异步函数

./servers/fetchUser.js

/** 
 * 获取用户信息
*/
export const fetchUser = () => {
  return new Promise((resole) => {
    setTimeout(() => {
      resole({
        name: 'moji',
        age: 24,
      })
    }, 2000)
  })
}

./test/fetchUser.test.js

import { fetchUser } from '../src/fetchUser';

test('fetchUser() 可以请求到一个用户名字为 moji', async () => {
  const data =  await fetchUser();

  expect(data.name).toBe('moji')
})

这里你可能看到这样一条报错

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

这是因为 @babel/preset-env 不支持 async await 导致的,这时候就需要对 babel 配置进行增强,可以安装 @babel/plugin-transform-runtime 这个插件解决

npm install --save-dev @babel/plugin-transform-runtime

同时改写 .babelrc

{
  "presets": ["@babel/preset-env", "@babel/preset-typescript"],
  "plugins": ["@babel/plugin-transform-runtime"]
}

再次运行就不会出现报错了

如何为前端编写单元测试?从这篇入门指南开始学习!,程序人生,软件测试,自动化测试,前端,单元测试,学习,javascript,测试工具,程序人生

.toContain 匹配对象中是否包含

./test/toContain.test.js

const names = ['liam', 'jim', 'bart'];

test('匹配对象是否包含', () => {
  expect(names).toContain('jim');
})

检查一些特殊的值(null,undefined 和 boolean)文章来源地址https://www.toymoban.com/news/detail-790242.html

toBeNull 仅匹配 null
toBeUndefined 仅匹配 undefined
toBeDefined 与…相反 toBeUndefined
toBeTruthy 匹配 if 语句视为 true 的任何内容
toBeFalsy 匹配 if 语句视为 false 的任何内容

检查数字类型(number)
toBeGreaterThan 大于
toBeGreaterThanOrEqual 至少(大于等于)
toBeLessThan 小于
toBeLessThanOrEqual 最多(小于等于)
toBeCloseTo 用来匹配浮点数(带小数点的相等)

到了这里,关于如何为前端编写单元测试?从这篇入门指南开始学习!的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot单元测试入门指南

    JUnit是一个成熟和广泛应用的Java单元测试框架,它提供了丰富的功能和灵活的扩展机制,可以帮助开发人员编写高质量的单元测试。通过JUnit,开发人员可以更加自信地进行重构、维护和改进代码,同时提高代码质量和可维护性。 在使用Spring Boot进行单元测试时,以下是一些

    2024年02月15日
    浏览(43)
  • Python单元测试之道:从入门到精通的全面指南

    在这篇文章中,我们会深入探讨Python单元测试的各个方面,包括它的基本概念、基础知识、实践方法、高级话题,如何在实际项目中进行单元测试,单元测试的最佳实践,以及一些有用的工具和资源 测试是软件开发中不可或缺的一部分,它能够帮助我们保证代码的质量,减少

    2024年02月16日
    浏览(41)
  • 【单元测试】--编写单元测试

    一、编写第一个单元测试 编写第一个单元测试通常包括以下步骤。以下示例以C#和NUnit为例: 创建测试项目 : 在Visual Studio中,创建一个新的Class Library项目,这将是你的单元测试项目。 在解决方案资源管理器中,右键点击项目,选择 “管理 NuGet 包”,然后搜索并安装NUnit框

    2024年02月07日
    浏览(44)
  • 单元测试:优雅编写Kotlin单元测试

    一、MockK简介 MockK是一款功能强大、易于使用的Kotlin mocking框架。在编写 单元测试 时,MockK能够帮助我们简化代码、提高测试覆盖率,并改善测试的可维护性。除了基本用法外,MockK还提供了许多额外的功能和灵活的用法,让我们能够更好地模拟对象行为、验证函数调用,并在

    2024年02月10日
    浏览(46)
  • 【Maven教程】(三)基础使用篇:入门使用指南——POM编写、业务代码、测试代码、打包与运行、使用Archetype生成项目骨架~

    到目前为止,已经大概了解并安装好了Maven环境, 现在,我们开始创建一个最简单的 Hello World 项目。如果你是初次接触 Maven, 建议按照本文的内容 一步步地编写代码并执行, 其中可能你会碰到一些概念暂时难以理解,但不用着急,记下这些疑难点,我在后续文章中会逐一进行

    2024年02月11日
    浏览(38)
  • 【go语言开发】编写单元测试

    本文主要介绍使用go语言编写单元测试用例,首先介绍如何编写单元测试,然后介绍基本命令的使用,最后给出demo示例 在go语言中编写单元测试时,使用说明 测试文件命名 :在 Go 语言中,测试文件的命名应与被测试的源代码文件相同,但以 “_test” 结尾。例如,如果你的源

    2024年02月04日
    浏览(45)
  • 利用ChatGPT协助编写单元测试

    ChatGPT自从2022年推出以来受到很多人的喜欢,此篇博客重点介绍如何修改Prompt来自动生成较理想的单元测试。如下图所示的一段代码,该class中有一个public方法toLocale(),其余都是private方法,toLocale()方法会调用private的方法。(备注:下面的方法特地写了比较多的分支逻辑,来验

    2024年02月11日
    浏览(40)
  • springboot基础学习 之编写单元测试和集成测试。

    编写单元测试和集成测试是保障应用程序质量的关键步骤。单元测试主要用于测试单个组件或模块的功能,而集成测试则关注不同组件之间的协作。下面分别介绍如何编写单元测试和集成测试。 单元测试(Unit Testing): 1. 选择测试框架: 选择适合你项目的测试框架,例如

    2024年02月02日
    浏览(45)
  • 软件测试中如何编写单元测试用例(白盒测试)

    目录 前言: 一、 单元测试的概念 二、开始测试前的准备 三、开始测试 四、完成测试 前言: 单元测试是软件测试中一种重要的测试方法,它是在代码级别进行测试,通过对每个模块或功能进行独立测试来保障代码的正确性和可靠性。单元测试可以有效地避免产生隐藏的代

    2024年02月09日
    浏览(51)
  • 系统学习Python——单元测试unittest:编写测试用例

    分类目录:《系统学习Python》总目录 相关文章: · 单元测试unittest:框架结构 · 单元测试unittest:测试固件 · 单元测试unittest:编写测试用例 · 单元测试unittest:执行测试用例 · 单元测试unittest:用例执行次序 · 单元测试unittest:内置装饰器 · 单元测试unittest:命令行执行测

    2023年04月08日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包