???? 令人愉快的 JavaScript 测试

栏目: JavaScript · 发布时间: 6年前

内容简介:Jest 是 Facebook 发布的一个开源的、基于 Jasmine 框架的 JavaScript 单元测试工具。提供了包括内置的测试环境 DOM API 支持、断言库、Mock 库等,还包含了 Snapshot Testing、 Instant Feedback 等特性。它自动集成了断言、JSDom、覆盖率报告等开发者所需要的所有测试工具,是一款几乎零配置的测试框架。并且它对同样是 Facebook 的开源前端框架 React 的测试十分友好。测试框架的作用是提供一些方便的语法来描述测试用例,以及对用
???? 令人愉快的 JavaScript 测试

1.1 Jest 是什么?

Jest 是 Facebook 发布的一个开源的、基于 Jasmine 框架的 JavaScript 单元测试工具。提供了包括内置的测试环境 DOM API 支持、断言库、Mock 库等,还包含了 Snapshot Testing、 Instant Feedback 等特性。它自动集成了断言、JSDom、覆盖率报告等开发者所需要的所有测试工具,是一款几乎零配置的测试框架。并且它对同样是 Facebook 的开源前端框架 React 的测试十分友好。

1.2 谁在用?

???? 令人愉快的 JavaScript 测试

1.3 卖点

  • Jest 是 Facebook 出品的一个测试框架,相对其他测试框架,其一大特点就是就是内置了常用的测试工具,比如自带断言、测试覆盖率工具,实现了开箱即用。
  • 而作为一个面向前端的测试框架, Jest 可以利用其特有的快照测试功能,通过比对 UI 代码生成的快照文件,实现对 React 等常见框架的自动测试。
  • 此外, Jest 的测试用例是并行执行的,而且只执行发生改变的文件所对应的测试,提升了测试速度。目前在 Github 上其 star 数已经破万;而除了 Facebook 外,业内其他公司也开始从其它测试框架转向 Jest ,比如 Airbnb 的尝试 ,相信未来 Jest 的发展趋势仍会比较迅猛。

2 课前准备

2.1 测试框架

测试框架的作用是提供一些方便的语法来描述测试用例,以及对用例进行分组。 测试框架可分为两种: TDD (测试驱动开发)和 BDD (行为驱动开发),我理解两者间的区别主要是一些语法上的不同,其中 BDD 提供了提供了可读性更好的用例语法,至于详细的区别可参见 The Difference Between TDD and BDD - Josh Davis 一文。 常见的测试框架有 Jasmine, Mocha 以及本文要介绍的 Jest 。

2.2 断言库

断言库主要提供语义化方法,用于对参与测试的值做各种各样的判断。这些语义化方法会返回测试的结果,要么成功、要么失败。常见的断言库有 Should.js, Chai.js 等。

测试覆盖率工具

用于统计测试用例对代码的测试情况,生成相应的报表,比如 istanbul

3 上手

初始化一个项目

mkidr $CODE_PATH && cd $CODE_PATH
npm init -y
npm i -D jest
复制代码

:warning: jest 默认不支持es6 如果需要测试es6 需要额外安装

npm i -D babel-jest babel-core babel-preset-env

并且增加 .babelrc 配置如下:

{
  "presets": ["env"]
}
复制代码

package.json 添加测试脚本

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watchAll"
  }
}
复制代码

这样我们的食材就准备好了:v:

3.1 第一个例子:chestnut: 1+2 = 3 让我们从写一个两个数相加的示例函数开始。首先,创建 sum.js sum.test.js 文件︰

touch sum.js sum.test.js
code .
复制代码

::sum.js::

export default function sum(a, b) {
  return a + b
}
复制代码

::sum.test.js::

import sum from './sum'
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})
复制代码

:warning: Jest 的测试脚本名形如**.test.js,不论 Jest 是全局运行还是通过npm test运行,它都会执行当前目录下所有的**.test.js 或 *.spec.js 文件、完成测试。

运行 npm run test

???? 令人愉快的 JavaScript 测试

以上是官网的一个小例子

3.2 新的尝试

你可以修改下 函数 sum 试试错误的结果

::sum.js::

export default function sum(a, b) {
  return a + b + 1
}
复制代码

运行 npm run test

???? 令人愉快的 JavaScript 测试

测试没通过:broken_heart:,如果我们学会了使用jest 那我们的工作流程将会变成:

需求分析 :arrow_right: 写单元测试 :arrow_right: 写代码 :arrow_right: 单元测试全部通过

那么就意味着单元测试全部通过就没bug了么?那可不一定

???? 令人愉快的 JavaScript 测试
那么为什么要写单元测试?详见写单元测试的重要性

4 常用的几个Jest断言

接下来写一个函数的集合 ::functions.js::和::functions.test.js::来演示一些常用的测试方法

touch functions.js functions.test.js
复制代码

::functions.js::

export default {
  add: (num1, num2) => num1 + num2,
}
复制代码

::functions.test.js::

import functions from './functions'
const { add } = functions
// toBe
test('Adds 2 + 2 to equal 4', () => {
  expect(add(2, 2)).toBe(4)
})
复制代码

4.1 not

在::functions.test.js::增加测试

...
// not
test('Adds 2 + 2 to NOT equal 5', () => {
  expect(functions.add(2, 2)).not.toBe(5);
});
...
复制代码

.not 修饰符允许你测试结果不等于某个值的情况,这和英语的语法几乎完全一样,很好理解。

4.2 toBeNull

::functions.js::

export default {
  add: (num1, num2) => num1 + num2,
	isNull: () => null
}
复制代码

在::functions.test.js::增加测试

// toBeNull
test('Should be null', () => {
  expect(isNull()).toBeNull();
});
复制代码

测试通过

4.3 toBeFalsy

toBeFalsy 判断值是否为false

// toBeFalsy
test('Should be falsy', () => {
  expect(undefined).toBeFalsy()
})
复制代码

4.4 toBeFalsy/toBe

// functions.js
createUser: () => {
  const user = { firstName: 'Brad' };
  user['lastName'] = 'Traversy';
  return user;
}
// functions.test.js
test('User should be Brad Traversy object', () => {
  expect(createUser()).toEqual({
    firstName: 'Brad',
    lastName: 'Traversy'
  });
});
复制代码

:warning: .toEqual 匹配器会递归的检查对象所有属性和属性值是否相等,所以如果要进行应用类型的比较时,请使用 .toEqual 匹配器而不是 .toBe

4.5 More

jest 使用的断言风格与社区相差无几。

// 数值对比
expect(8).toBeGreaterThan(7)
expect(7).toBeGreaterThanOrEqual(7)
expect(6).toBeLessThan(7)
expect(6).toBeLessThanOrEqual(6)

// Regex 正则匹配
expect('team').not.toMatch(/I/i)

// Arrays
expect(['john', 'karen', 'admin']).toContain('admin');
复制代码

更多断言方法参见:Expect · Jest

4.6 测试异步

JSONPlaceholder是一个提供免费的在线 REST API 的网站,我们在开发时可以使用它提供的 url 地址测试下网络请求以及请求参数。或者当我们程序需要获取一些假数据、假图片时也可以使用它。

// functions.js
fetchUser: () =>
	axios
    .get('https://jsonplaceholder.typicode.com/users/1')
    .then(res => res.data)
// functions.test.js
// Async Await
test('User fetched name should be Leanne Graham', async () => {
  expect.assertions(1)
  const data = await functions.fetchUser()
  expect(data.name).toEqual('Leanne Graham')
})
复制代码

上面我们调用了expect.assertions(1),它能确保在异步的测试用例中,有一个断言会在回调函数中被执行。这在进行异步代码的测试中十分有效。 doc.ebichu.cc/jest/docs/z… 具体的文档说明。

5 实战

5.1 chunkArray

来写个函数::chunkArray:: 将数组 array 拆分成多个 size 长度的区块,并将这些区块组成一个新数组。 如果array 无法被分割成全部等长的区块,那么最后剩余的元素将组成一个区块。 举例:

  • 实例1: 数组 [1,2,3,4,5,6,7,8,9,10] 拆分的size 为2 那么 最后输出结果为 [[1,2],[3,4],[5,6],[7,8],[9,10]]
  • 实例2: 数组 [1,2,3,4,5,6,7] 拆分的size 为3 那么输出结果为 [[1,2,3],[4,5,6],[7] 以上就是分析过程,也就是我们日常开发中的需求描述,现在我们把需求转换成单测 ::chunkArray.test.js::
// 确认chunkArray 已经被声明定义
test('chunkArray function exists', () => {
  expect(chunkArray).toBeDefined()
})

// 验证实例1
test('Chunk an array of 10 values with length of 2', () => {
  const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  const len = 2;
  const chunkedArr = chunkArray(numbers, len);
  expect(chunkedArr).toEqual([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]);
});
// 验证实例2
test('Chunk an array of 10 values with length of 2', () => {
  const numbers = [1, 2, 3, 4, 5, 6, 7]
  const len = 3
  const chunkedArr = chunkArray(numbers, len)
  expect(chunkedArr).toEqual([[1, 2, 3],[4, 5, 6], [7])
})
复制代码

接下来就是写chunkArray函数了,给出参考 ::chunkArray.js::

const chunkArray = (arr, len) => {
  // Init chunked arr
  const chunkedArr = []

  // Loop through arr
  arr.forEach(val => {
    // Get last element
    const last = chunkedArr[chunkedArr.length - 1]

    // Check if last and if last length is equal to the chunk len
    if (!last || last.length === len) {
      chunkedArr.push([val])
    } else {
      last.push(val)
    }
  })

  return chunkedArr
}
复制代码

如上,是开发流程,这个玩法在一些算法刷算法的社区也有体现,比如codewars

???? 令人愉快的 JavaScript 测试

写到这里我在思考单元测试的意义在哪?总结如下:

  1. 写单测的是对需求的梳理,动手写代码之前,让自己对于需求的理解更加明确。
  2. 单测像一个机器人在帮你值守模块的正确性,随着项目复杂和规模变大,单测能让开发者提前知道因为模块的修改和变动的影响范围。

6 测试覆盖率

Jest 内置了测试覆盖率工具istanbul,在::package.json::中增加配置

"collectCoverage": true,
"coverageReporters": ["json", "html", "text"]
复制代码

npm run test 在命令行中会出测试覆盖率,

???? 令人愉快的 JavaScript 测试
同时在工程目录下生成 coverage

目录有网页版的报告

???? 令人愉快的 JavaScript 测试

X. 参考文献

  1. 使用Jest测试JavaScript (入门篇)
  2. The Difference Between TDD and BDD - Josh Davis
  3. 前端测试框架 Jest
  4. 无单测、不编码——写单元测试的重要性-HollisChuang’s Blog
  5. Jest Crash Course - Unit Testing in JavaScript - YouTube

以上所述就是小编给大家介绍的《???? 令人愉快的 JavaScript 测试》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

计算理论导论

计算理论导论

塞普斯 / 机械工业出版社 / 2002-8 / 39.0

This book——by a noted authority and educator in the field——presents computer science theory from a uniquely intuitive,“big picture”perspective.The author grounds his clear and interesting study on ......一起来看看 《计算理论导论》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具