关于前端:一文带你了解-Jest-单元测试

前言

前端测试遍及度尽管不算高,但对我的项目代码品质以及前期保护我的项目上却有很大的作用。本文会抛砖引玉的讲下 Jest 单元测试,因为很多 API 具体看官网就能够了。

什么是 Jest

Jest 是用来创立、执行和构建测试用例的一个 JavaScript 测试
库。能够在任何我的项目中装置应用它,如 Vue/React/Angular/Node/TypeScript 等。

应用 Jest 的益处

目前前端测试在开发利用场景次要是 UI 组件库,但其实不止是组件库,日常我的项目开发也能够退出单元测试,保障我的项目的稳固。

它的益处有哪些呢?

  • 速度快,应用简略和容易配置
  • 通过编写测试,让代码呈现 bug 的概率更小
  • 有利于写出健壮性更好的代码,我的项目的可维护性加强
  • 好的测试,就具备文档解释的作用
  • 缩小回归测试中的 bug

举个栗子

比方写一个加法和减法函数

function add(a, b) {
  return a + b
}

function minus(a, b) {
  return a - b
}

咱们从最简略的函数开始动手讲起来,假如要对改函数进行测试,咱们测试的到底是什么,即是他的性能,调用函数时传入两个参数,返回后果为他们相加的值。比方传入add(1, 1),咱们冀望后果是返回相加后果2
写成代码能够这样expect(add(1, 1)) === 2,再封装得语义化一点的话,expect(add(1, 1)).toBe(2)为咱们冀望的测试函数,来实现这个函数:

function expect(result) {
  return {
    toBe(actual) {
      if (result !== actual) {
        throw new Error(`与预期后果不相等。理论后果:${actual},冀望后果:${result}`)
      }
    },
  }
}

应用:

expect(add(1, 1)).toBe(2)
expect(minus(2, 1)).toBe(1)
expect(add(2, 2)).toBe(3) // Error: 与预期后果不相等。理论后果:3,冀望后果:4

测试不通过咱们就让它抛出谬误

这样写的话理论有点凌乱,因为一眼看上来不晓得在测试啥,于是,咱们多封装一个函数,代表咱们正在做什么测试

// desc 用来形容测试行为, fn则为咱们执行测试的函数
function test(desc, fn) {
  try {
    fn()
    console.log(`${desc}通过测试`)
  } catch (e) {
    console.log(`${desc}测试不通过 ${e}`)
  }
}

test('加法测试', () => {
  expect(add(1, 1)).toBe(2)
})

test('减法测试', () => {
  expect(minus(2, 1)).toBe(1)
})

执行代码,输入

加法测试通过测试
减法测试通过测试

这就是咱们入手的最简略的测试了,因为咱们函数比较简单,所以你可能会感觉在做着啰嗦的工作。

从流程上看,一个简略的测试流程(输出 —— 预期输入 —— 验证后果)如下:

  1. 引入要测试的函数
  2. 传入测试函数执行后果
  3. 定义预期输入
  4. 查看函数是否返回了预期的输入后果

初始化环境

创立一个空文件夹进入初始化

npm init -y

装置 jest

yarn add --dev jest

package.json配置脚本命令

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

新建文件babel.config.js,装置依赖并配置

yarn add --dev babel-jest @babel/core @babel/preset-env
// babel.config.js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: {
          node: 'current',
        },
      },
    ],
  ],
}

接下来咱们每段介绍都会采取example.js外面写代码,再到*.test.js文件引入example.js的函数并测试。

匹配器 Matchers

应用 Matchers 让你能够用各种形式测试你的代码

跟咱们下面写的代码一样,jest 提供了test,expect,toBe办法

example.js

export const multiply = (a, b) => {
  return a * b
}

新建文件matcher.test.js

import { multiply } from './example'

test('测试乘法', () => {
  expect(multiply(4, 2)).toBe(8)
})

toBe 应用 Object.is 来测试准确相等。 如果想要查看对象的值,应用 toEqual 代替。

matcher.test.js

test('测试对象是否相等', () => {
  const data = { name: 'jacky' }
  data['age'] = 23
  expect(data).toEqual({ name: 'jacky', age: 23 })
})

运行yarn test,可看到控制台显示两个测试通过;如不通过控制台也会提醒哪个测试用例出错。

当运行该命令的时候,jest 会主动搜查我的项目中有.test.js后缀的文件,并运行测试。

当然,也有其余匹配器

test('匹配器', () => {
  const n = null
  const isTrue = true
  const value = 3
  expect(n).toBeNull() // 只匹配 null
  expect(n).toBeDefined() // 只匹配 undefined
  expect(n).not.toBeUndefined() // 与 toBeUndefined 相同
  expect(isTrue).toBeTruthy() // 匹配任何 if 语句为真

  expect(value).toBeGreaterThan(2)
  expect(value).toBeLessThan(5)
  expect(value).toBeLessThanOrEqual(3)
})

测试异步代码

example.js

import axios from 'axios'

export const fetchData = () => {
  return axios.get('https://jsonplaceholder.typicode.com/todos/1')
}

async.test.js

import { fetchData } from './example'

test('测试 async await', async () => {
  const data = await fetchData()
  expect(data.data.id).toBe(1) // 申请api返回的id值是否为1
})

test('测试 Promise', () => {
  return fetchData().then(data => {
    expect(data.data.id).toBe(1)
  })
})

mock 函数

如果咱们有很多异步申请的函数须要测试,会使测试变得迟缓,那么上述用实在接口申请的形式就不太适宜了;或者想要捕捉对函数的调用,这时就须要 Mock,它能够做到:

  1. 捕捉对函数的调用,以及 this 和调用程序
  2. 它容许测试时配置返回值
  3. 扭转函数的外部实现

mock.test.js

import axios from 'axios'
import { fetchData } from './example'
jest.mock('axios') // 主动模仿 axios 模块。

test('测试申请接口', async () => {
  axios.get.mockResolvedValue({ data: 'hello' }) // 返回假数据 { data: 'hello' } 用于测试
  await fetchData().then(data => {
    expect(data).toEqual({ data: 'hello' })
  })
})

可能你会想,这样测试有什么用,都是假数据,作为前端这边,咱们只有确保函数被调用和失常返回后果就够了;当然这个例子是比较简单,波及简单接口测试的话还有其余 API 可用。其余更深刻具体的接口申请测试是后端要做的。

example.js减少一个函数

// 执行该函数须要传入一个回调函数,而后回调函数传递一个参数并执行
export function runFn(fn) {
  fn('函数执行了')
}

如果这是一个简单的函数,咱们并不想测试就让它执行,就能够用 mock 来测试

test('函数被调用了', () => {
  const fn = jest.fn()
  runFn(fn) // 调用函数

  expect(fn).toHaveBeenCalled() // 冀望fn被调用了
  expect(fn).toHaveBeenLastCalledWith('函数执行了') // 函数fn被调用时的参数是否合乎
})

此外,还能够自定义设置返回参数,测试被执行次数等等,具体查官网文档。

Snapshot

每当咱们想要确保 UI(指 html css 等)或者配置文件不会扭转时,能够应用快照测试。

这里只举例配置文件

example.test.js

export function getConfig() {
  return {
    port: 8080,
    url: 'http://localhost:3000',
  }
}

snapshot.test.js

import { getConfig } from './example'

test('测试配置文件有没有更改', () => {
  expect(getConfig()).toMatchSnapshot()
})

运行npm run test,第一次运行,会发现文件所在位置生成一个文件夹__snapshots__,外面有个snapshot.test.js.snap文件,外面内容是:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`测试配置文件有没有更改 1`] = `
Object {
  "port": 8080,
  "url": "http://localhost:3000",
}
`;

外面保留了咱们配置信息,如果你不小心改了配置信息的话,在执行测试就会报错,因为与这个文件的内容不合乎;这时你就晓得了你改到不应该改的地位了
比方把端口好改成8081,再运行npm run test

但如果真的要改的话呢,那就按提醒外面的信息做,更新快照,执行:

npm test -- -u

执行后就会在终端看到:

Snapshots:   1 updated, 1 total

这次再执行测试就不会报错了,就把配置信息更新了。

Timers Mocks

当要测试有定时器的函数时,比方过 5 秒执行什么工作,对于咱们来说并不是很不便,要真正期待它通过肯定工夫执行,这是能够模仿定时器函数。

example.js

export function timerGame(callback) {
  console.log('Ready....go!')
  setTimeout(() => {
    console.log("Time's up -- stop!")
    callback && callback()
  }, 3000)
}
import { timerGame } from './example'

jest.useFakeTimers()

test('测试肯定工夫后执行函数', () => {
  const fn = jest.fn()
  timerGame(fn)
  jest.advanceTimersByTime(3000) // 把工夫提前了三秒
  expect(fn).toBeCalled() // 函数被调用了
  expect(fn).toHaveBeenCalledTimes(1) // 函数被执行了1次
})

测试覆盖率

减少一个脚本命令,将测试覆盖率信息输入为报告

"coverage": "jest --coverage"

执行该命令

能够在终端看到生成了报告,上述例子的代码测试覆盖率是 100%,这是因为测试的函数比较简单,所以轻易 100%。

同时我的项目根目录也胜利了一个文件夹coverage,外面的lcov-report目录里生成 HTML 文件,能够通过浏览器关上查看。

如下:

TDD 与 BDD

当初风行的单元测试格调次要有 TDD(测试驱动开发)和 BDD(行为驱动开发)两种。比拟显著的区别是一个是先写测试用例再写代码,一个是先写代码再写测试用例。

TDD

TDD 的开发流程个别是先编写测试用例,再编写代码。

比方写一个 Todolist,咱们一开始先想好它的性能和留神点,每一个性能都具备对应的测试用例,编写测试用例,没写代码之前必定是通不过的;而后写完开始编写代码,使测试用例通过测试,再持续反复步骤,实现开发。这样写进去的代码品质和维护性也会更好。

所以 TDD 个别就是作为单元测试,即是繁多组件性能上测试得较为欠缺,但几个组件加起来测试这种却不肯定能测试欠缺。

单元测试利用比拟多的是函数库,可对每个函数进行独自粗疏测试。

单元测试比拟独立,但过于独立也会暗藏一些潜在问题无奈测到,这个时候就有集成测试辅助了。

BDD

BDD 的开发流程个别是先编写代码,再写测试用例。

这个就比拟合乎咱们以往的开发模式,不过 BDD 更关注的是整体的行为是否合乎预期,个别联合集成测试,也就是说整体好多个组件整体业务测试这种。

集成测试重点关怀的是后果,而不是代码,更适宜业务开发。

结语

两种测试格调都有它本人的优缺点,这里只是举例一部分;整体简略了解下来,前端测试能让咱们写出更好的代码,缩小 bug 的产生;而且,在保护一些老我的项目方面,也是有劣势,比方你在改变旧我的项目代码的时候,可能怕影响其余组件或性能但你无奈通晓,但如果有测试,间接改完运行所有测试代码就晓得有没有影响到其余的性能代码了;而如果老我的项目没有写单元/集成测试,为了更好的保护,你也能够退出测试代码。

如果要学习更多无关 Jest 测试的内容,当然是间接去 Jest 官网 看了,而后在理论开发使用中才会逐步领会到它带来的益处。

本文代码地址

  • ps: 集体技术博文 Github 仓库,感觉不错的话欢送 star,给我一点激励持续写作吧~

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理