单元测试是什么

单元测试就是测试最小单元(一个方法,一个组件)。

为什么要写单元测试

虽然大家都已经回答过无数次这个问题了,但是那并不代表我确实理解了。

我以前也没有写过单元测试,这也是为什么要来记录这篇文章的原因。

对于写单元测试,我个人的直观感触是可以促使自己编写更“纯”的函数或方法。这其实对于代码的阅读、维护、重构等都带来了优惠。你可以更加明确这个函数的作用,至少会想方设法让自己的函数所影响的范围进行缩减、可控。

再者说,对于 CI/CD,单元测试又是非常有必要的一个环节。

我也相应的搜索了一些网络上小伙伴们的解答,大家无外乎都是在围绕着可阅可用易于重构自我提升这些方面在阐述,总之写单元测试是一件好的事情,如果你有能力有时间,那么请尝试一下并且持续的写下去吧。

测试工具

经过一些查阅,mocha + chai 仿佛是一个比较普遍的选择。

mocha 是一个测试框架,它对异步代码的追踪测试很有自信。

chain 是一个断言库

此外还有一个 jest,它是一个大而全的测试框架,它使用内置的 matchers 断言。

mocha + chai

初始化一个测试项目

# 新建一个文件夹
mkdir mocha-chai
# 进入文件
cd mocha-chai
# 初始化
npm init -y
# 安装
yarn add mocha chai

接下来准备一个源码文件 index.js 和 测试文件 index.spec.js

新建 index.js

// index.js
module.exports.getAbs = (x, y) => {
  return Math.abs(x - y)
}

新建 index.spec.js

为什么必须是 .spec.js?我去查了一下,结果如下:

约定俗成的术语。
Unit Testing Specification(单元测试基准)的缩写,很多框架默认回去找这些后缀的文件。

官方也给出一段相关的内容,不太确定是否还有其他处。

Test files can be specified using spec, e.g., "spec": "test/**/*.spec.js".
// index.spec.js
const assert = require('chai').assert
const getAbs = require('./index').getAbs

describe('to test getAbs()', () => {
  it('should return 1 when the value is 1,2', () => {
    assert.equal(getAbs(1, 2), 1, 'getAbs(1,2) equal 1')
  })
  it('should return 1 when the value is 2,1', () => {
    assert.equal(getAbs(2, 1), 1, 'getAbs(2,1) equal 1')
  })
  it('should return 0 when the value is 1,1', () => {
    assert.equal(getAbs(1, 1), 0, 'getAbs(1,1) equal 0')
  })
})

修改 package.json

主要是 test 命令:

"scripts": {
    "test": "mocha index.spec.js"
},

run mocha

执行:

yarn test
yarn run v1.21.1
$ mocha index.spec.js


  to test getAbs()
    √ should return 1 when the value is 1,2
    √ should return 1 when the value is 2,1
    √ should return 0 when the value is 1,1


  3 passing (8ms)

Done in 0.46s.

未通过的 mocha

现在,在 index.spec.js 文件中追加一个 it()

it('should return 0 when the value is not present', () => {
    assert.equal(getAbs(), 0, 'getAbs() equal 0')
})

之后再次执行:

yarn test
yarn run v1.21.1
$ mocha index.spec.js


  to test getAbs()
    √ should return 1 when the value is 1,2
    √ should return 1 when the value is 2,1
    √ should return 0 when the value is 1,1
    1) should return 0 when the value is not present


  3 passing (18ms)
  1 failing

  1) to test getAbs()
       should return 0 when the value is not present:

      getAbs() equal 0
      + expected - actual

      -NaN
      +0

      at Context.<anonymous> (index.spec.js:15:12)
      at processImmediate (internal/timers.js:439:21)



error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

总之就是未得到预期的值得意思,很明显这是因为没有传递参数导致的。也就是说在不给函数传递参数的时候,函数的行为有些不符合预期的效果。

所以稍作修改再来一次。

修改 index.js

// index.js
module.exports.getAbs = (x = 0, y = 0) => {
  return Math.abs(x - y)
}

再次执行结果如下:

yarn run v1.21.1
$ mocha index.spec.js


  to test getAbs()
    √ should return 1 when the value is 1,2
    √ should return 1 when the value is 2,1
    √ should return 0 when the value is 1,1
    √ should return 0 when the value is not present


  4 passing (7ms)

Done in 0.47s.

所以说,实际上 unit testing 视情况来看,需要编写很多的断言,上面的测试虽然是通过了,但实际上还有有其他的问题存在的,极端点来讲比如传递的参数并不是一个数字:

it('should return 0 when the value is not a number', () => {
    assert.equal(getAbs([1, 2, 3], 3), 0, 'getAbs() equal 0')
})

JavaScript 并不像强类型语言那样强硬,所以很多时候即便是错误的使用一个函数也只有在执行起来之后才能浮现错误。

嗯...所以说,单元测试中的断言所能覆盖的情况要相对比较广泛才可以,否则也只是片面的结论而已。

总感觉一不小心就会走火入魔...

小结

unit testing 目的是为了使程序更加的健壮,稳定。但是相应的是需要编写更多的代码才能达到这个目的,所以如何正确的把握这个度,应该是一个值得思考的问题。

所以也只有更多的实践才可以找出一个比较满意的答案吧。


youbei
318 声望70 粉丝