51Testing软件测试论坛

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

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

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

[转贴] 讨论太多的工具了,可你知道为什么要进行单元测试吗?

[复制链接]
  • TA的每日心情
    无聊
    3 天前
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2020-12-18 09:39:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    为什么要做单元测试
      在开始之前,我们先思考这样一个问题,我们为什么要做单元测试?
      不扯犊子直接说吧,第一点,用数据、用茫茫多的测试用例去告诉使用者,你的程序是多么鲁棒健壮;第二点,把它作为一种素养去培养吧,当你按照一系列规范去做事,那么你做出来的东西,我想是有品质在的。
      jest的安装
      在确保你的电脑装有node环境的情况下,我们通过mkdir jest-study && cd jest-study来初始化项目,然后我们通过npm init -y初始化npm环境。
      执行npm i jest babel-jest @babel/core @babel/preset-env 命令安装相应的依赖包,因为后面的例子是基于ES Module的语法编写的,所有需要安装babel进行语法转义。当然你也可以选择直接用CommonJS的写法,node天然支持的。
      jest的相关配置
      package.json中相关scripts
      这里笔者罗列了常用的通用的一些关于jest的脚本,后面测试结果会陆续补充一些测试脚本,以上的脚本都编写在package.json文件下的scripts脚本下面。
      通用写法
      "test": "jest" : 这个比较傻瓜式,当执行npm run test这条命令是会去对test目录下的所有文件进行相应的jest测试。
      "test:help": "jest --help": 顾名思义,如果你不想全局安装jest,又想看看到底有哪些cli命令的话,就它了。
      "test:debug": "jest --debug": 顾名思义,debug啊。
      "test:verbose": "jest --verbose": 以层级显示地方式在控制台展示测试结果。
      "test:noCache": "jest --no-cache": 顾名思义,就是设置有没有缓存,有缓存的话会快点。
      "test:init": "jest --init": 执行这句就是在根目录创建一个jest.config.js文件,它在创建的时候有很多选择项给你的。
      "test:caculator": "jest ./test/caculator.test.js": 单文件测试。
      "test:caculator:watch": "jest ./test/caculator.test.js --watch": 单文件监视测试。
      "test:watchAll": "jest --watchAll": 监视所有文件改动,测试相应的测试。
      大致基础类的脚本测试就总结到这里,接下来我们看下jest.config.js的相关配置。
      jest.config.js中相关配置
      里面配置的参数太多了,有些配置了以后就可以不再package.json文件下写相应的脚本,这里笔者阉割一部分,列举最常见的几个。
    1. module.exports = {

    2.     // Automatically clear mock calls and instances between every test

    3.     clearMocks: true,

    4.     // The directory where Jest should output its coverage files

    5.     coverageDirectory: "coverage",

    6.     // The test environment that will be used for testing

    7.     testEnvironment: "node",

    8.   }
    复制代码
    babel相关配置


    1.  {

    2.     "presets": [["@babel/preset-env", {

    3.         "targets": {

    4.           "node": "current"

    5.         }

    6.       }

    7.     ]]

    8.   }
    复制代码
    这里就是配置了相应的语法转换预设,如果后期有其他需求,可以通过plugins去配置写补丁转义器,相关内容这里就不做介绍了,可以看下笔者之前写的关于babel的文章。  测试结果
      考虑到把相关信息打在控制台上,第一,控制台可能会出现一处的情况;第二,在查看结果内容多的话可能引起眼睛不适,所有就有了楼下几种可能。
      测试覆盖率
      在package.json中的scripts下配置 "test:coverage": "jest --coverage"后,然后执行相应脚本,就会在根目录输出一个coverage文件夹,里面包含了相应的测试脚本。当然控制台也会输出的。

     html显示  执行 npm i jest-html-reporter安装这个模块包(这里提及一下,在npm版本大于5.x以后,可以默认不加--save这种参数),然后在jest.config.js中配置如下:
    1.   reporters: [

    2.       "default",

    3.       ["./node_modules/jest-html-reporter", {

    4.         "pageTitle": "Test Report"

    5.       }]

    6.     ],
    复制代码

    执行相关的jest测试后,会在根目录生成一个test-report.html文件,打开后形如:

    json显示  在package.json中配置scripts脚本 "test:exportJson": "jest --json --outputFile=./export/reporter.json",然后执行npm run test:exportJson就会输出相应的json报告文件,控制台也会以json的形式输出相应信息。
      断言(expect)
      断言库的种类有很多,例如、assert、should、expect、chai等等,楼下的例子,笔者均以expect作为讲解。
      not
      先说个最简单的expect(received).not.toBe(expected),这句话的意思就是表示否对,表示我断言、接收值不等于期望值。
      toBe(expected)
      这个API常用于断言,值类型的期望值,也就是boolean、string、number、这些类型的,用它做引用类型的断言是不合适也不可取的。
      to_be.test.js
    1.  describe('#toBe', () => {

    2.     it('to be string', () => {

    3.       expect('hello world').toBe('hello world')

    4.     })

    5.     it('to be number', () => {

    6.       expect(1 + 1).toBe(2)

    7.     })

    8.     it('to be boolean', () => {

    9.       expect(true).toBe(true)

    10.       expect(false).toBe(false)

    11.     })

    12.     it('to be null', () => {

    13.       expect(null).toBe(null)

    14.     })

    15.     it('to be undefined', () => {

    16.       expect(undefined).toBe(undefined)

    17.     })

    18.   })
    复制代码
    toEqual(expected)  通俗的理解就是等于, 可以是值类型也可以是引用类型的相等。
      to_equal.test.js
    1.  test('#toEqual', () => {

    2.     expect('hello world').toEqual('hello world')

    3.     expect(110).toEqual(110)

    4.     expect(true).toEqual(true)

    5.     expect(false).toEqual(false)

    6.     expect(null).toEqual(null)

    7.     expect(undefined).toEqual(undefined)

    8.     expect([1, 2, 3, 4]).toEqual([1, 2, 3, 4])

    9.     expect({ name: 'ataola' }).toEqual({ name: 'ataola' })

    10.   })
    复制代码

     toContain(expected) && toContainEqual(expected)  toContain()跟的期望值是值类型的,而toContainEqual() `可以是值类型也可以是引用类型,表示包含。
      to_contain.test.js
    1.  test('#toContain', () => {

    2.     expect([1, 2, 3, 4]).toContain(1)

    3.     expect([[1, 2], [3, 4], [5, 6]]).toContainEqual([1, 2])

    4.   })
    复制代码
    数值比较  楼下expect后面跟的英语的字面量意思就是其方法的作用,分别是,大于、大于等于、小于、小于等于、相似于(接近于),这里值得一题的事最后一个toBeCloseTo(),思考一下改成toBe()可以吗?很显然不行,其算出来的结果是0.30000000000000004,究其原因是js采用的是双精度浮点表示。
      number_compare.test.js
    1.  test('number compare', () => {

    2.     expect(3).toBeGreaterThan(2)

    3.     expect(3).toBeGreaterThanOrEqual(2.5)

    4.     expect(3).toBeLessThan(4)

    5.     expect(3).toBeLessThanOrEqual(3.5)

    6.     expect(0.1 + 0.2).toBeCloseTo(0.3) // <0.05 passed

    7.   })
    复制代码

    toMatch(expected)  顾名思义,字符串匹配,它支持字符串和正则,/^(\w+)\1+$/匹配的是一个字符串可以由其字串通过n次组合而成的字串(leetcode一道题目),所有其匹配到的是tao。
      string_match.test.js
    1. test('string match', () => {

    2.     expect('ataola').toMatch('ataola')

    3.     expect('ataola').not.toMatch('aloata')

    4.     expect('taotao'.match(/^(\w+)\1+$/)[0]).toMatch('tao')

    5.   })
    复制代码

    内置的一些基本类型值  null、undefined、真假值比较特殊,所有这里单独有个方法表示它们。
      truthiness.test.js
    1.  // toBeNull、 toBeUndefined 、 toBeDefined 、 toBeTruthy、 toBeFalsy

    2.   test('truthiness', () => {

    3.     expect(null).toBeNull()

    4.     expect(undefined).toBeUndefined()

    5.     expect('i am defined').toBeDefined()

    6.     expect(true).toBeTruthy()

    7.     expect(false).toBeFalsy()

    8.   })
    复制代码

    ToThrow(expected)  这里是处理相关异常的, 后面可以什么都不根,也可以跟个Error,或者相应的Error输出信息。
      exceptions.test.js
    1. function gaoError() {

    2.     throw new Error('二营长开炮,开炮,开炮。。。')

    3.   }

    4.   test('#ToThrow', () => {

    5.     expect(gaoError).toThrow()

    6.     expect(gaoError).toThrow(Error)

    7.     expect(gaoError).toThrow('二营长开炮,开炮,开炮。。。')

    8.   })
    复制代码

      好了,到这里比较基础和通用的API就介绍到这里。接下来,我们通过自己编写相关代码去巩固下楼上的知识,这里笔者提供两个demo,一个是关于异步获取数据的断言、一个是实现一个计算器类的断言。
      异步
      我们通过request-promise这个库去请求https://v1.hitokoto.cn去获取相应的json数据,然后进行断言。
      hitokoto.js
    1.  import rp from 'request-promise'

    2.   const getHitokoto = async () => {

    3.     const res = await rp('https://v1.hitokoto.cn')

    4.     return res

    5.   }

    6.   export default getHitokoto
    复制代码

     hitokoto.test.js
    1. import getHitokoto from '../src/hitokoto'

    2.   test('hitokoto', async () => {

    3.     const data = await getHitokoto()

    4.     expect(data).not.toBeNull()

    5.   })
    复制代码

    这里就意思下,读者可以把data里面的数据解构出来,进行相应的断言。  计算器
      这里模拟了笔者手机上的计算器,实现了加减乘除清零计算等功能。
      caculator.js

    1. class CaCulator {

    2.     constructor() {

    3.       this.result = 0

    4.     }

    5.     add(...args) {

    6.       let { result } = this

    7.       result += args.reduce((pre, cur) => pre + cur)

    8.       this.result = result

    9.       return this

    10.     }

    11.     reduce(...args) {

    12.       let { result } = this

    13.       result -= args.reduce((pre, cur) => pre + cur)

    14.       this.result = result

    15.       return this

    16.     }

    17.     multiply(...args) {

    18.       let { result } = this

    19.       if (result) {

    20.         for (const val of args) {

    21.           result *= val

    22.         }

    23.       }

    24.       this.result = result

    25.       return this

    26.     }

    27.     divide(...args)  {

    28.       let { result } = this

    29.       const has_zero = args.some(item => item === 0)

    30.       if (has_zero) {

    31.         result = '数学体育老师教的吗?'

    32.       } else {

    33.         for (const val of args) {

    34.           result /= val

    35.         }

    36.       }

    37.       this.result = result

    38.       return this

    39.     }

    40.     clear() {

    41.       this.result = 0

    42.       return this

    43.     }

    44.     exec() {

    45.       const { result } = this

    46.       if (typeof result === 'string') {

    47.         this.result = 0

    48.       }

    49.       return result

    50.     }

    51.     init(n) {

    52.       this.result = typeof n === 'number' ? n : 0

    53.       return this

    54.     }

    55.   }

    56.   export default CaCulator
    复制代码

    caculator.test.js
    1. import Caculator from '../src/caculator'

    2.   const caculator = new Caculator()

    3.   describe('test Caculator', () => {

    4.     test('#add', () => {

    5.       expect(caculator.add(1).exec()).toBe(1)

    6.       expect(caculator.clear().add(1, 2, 3).exec()).toBe(6)

    7.       caculator.clear()

    8.     })

    9.     

    10.     test('#reduce', () => {

    11.       expect(caculator.reduce(1).exec()).toBe(-1)

    12.       expect(caculator.clear().reduce(1, 2, 3).exec()).toBe(-6)

    13.       caculator.clear()

    14.     })

    15.     

    16.     test('#multiply', () => {

    17.       expect(caculator.multiply(1).exec()).toBe(0)

    18.       expect(caculator.init(1).multiply(2, 3, 4, 5).exec()).toBe(120)

    19.       caculator.clear()

    20.     })

    21.     

    22.     test('#divied', () => {

    23.       expect(caculator.divide(0).exec()).toBe('数学体育老师教的吗?')

    24.       expect(caculator.divide(1, 2).exec()).toBe(0)

    25.       expect(caculator.init(100).divide(2, 2).exec()).toBe(25)

    26.     })

    27.   })
    复制代码
     这里笔者只是罗列了日常开发中常用的断言API,具体的还是要参见官方文档这样的一手资料,希望能起到抛砖引玉的效果。













    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

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

    使用道具 举报

    本版积分规则

    关闭

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

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

    GMT+8, 2024-11-24 07:00 , Processed in 0.066366 second(s), 25 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

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