前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# 测试用例 mocha,should,istanbul

30 秒速记

  • 三件套分工:mocha 跑用例,should 写断言,istanbul 统计覆盖率
  • 现在的选择:istanbul 命令行已停更,用 nyc 或 c8;Node 20 起也有内置的 node --test 和覆盖率
  • 测试要稳定可重复:时间、网络、数据库、随机数都隔离或 mock 掉
  • 覆盖率只说明「代码跑过」,不说明「结果对」,重点盯分支、异常和边界
  • 异步用例 return 一个 Promise 或用 async 函数,别和 done 混用

这三个工具各管一件事:mocha 负责组织和运行测试,should 负责写「结果应该是什么」的断言,istanbul 负责统计哪些代码被测到了。 这套组合比较老,现在我会这么选:运行器还是 mocha 或者换 vitest,断言用 Node 自带的 assert 或 chai,覆盖率用 nyc 或 c8,istanbul 的命令行早就不维护了,nyc 就是它的继任者。不管用哪套,思路是一样的:用例要覆盖正常值、边界和异常,覆盖率报告用来找漏测的分支,而不是追求一个好看的百分比。

// test/sum.test.js,运行:npx mocha
const assert = require('node:assert')
const sum = (a, b) => a + b
describe('sum', () => {
  it('1 + 2 = 3', () => assert.strictEqual(sum(1, 2), 3))
  it('async 用例直接 return / await', async () => {
    const v = await Promise.resolve(sum(2, 2))
    assert.strictEqual(v, 4)
  })
})
@前端进阶之旅: 代码已经复制到剪贴板

理解「测试用例 mocha,should,istanbul」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 testing 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

mocha + should + istanbul 是早期 Node 项目的经典测试组合,三者分工明确。

mocha 是测试运行器,提供 describe / it 组织用例,before / after / beforeEach / afterEach 做准备和清理,负责收集结果和退出码。它本身不带断言。

should 是断言库,它在 Object.prototype 上挂了一个 should 属性,所以能写成 (5).should.above(3)。缺点是 null 和 undefined 上没有这个属性,要改用 should(x).be.null()。同类的还有 chai(expect / should / assert 三种风格都有)和 Node 内置的 node:assert。

istanbul 是覆盖率工具,会统计四个维度:语句、分支、函数、行。它的命令行 istanbul cover 已经停止维护,现在用的是 nyc(同一个团队维护,基于 istanbul 的插桩库)或者 c8(直接用 V8 引擎内置的覆盖率数据,不用插桩)。

按今天的习惯,一个最小的配置可以这样写:

{
  "scripts": {
    "test": "mocha",
    "cov": "c8 mocha"
  },
  "devDependencies": { "mocha": "^10.0.0", "c8": "^10.0.0" }
}
@前端进阶之旅: 代码已经复制到剪贴板

npm run 会自动把 node_modules/.bin 加进 PATH,所以脚本里直接写 mocha 用的就是项目本地安装的版本,不需要全局安装,也不需要 Makefile。Node 20 以后还可以用零依赖的内置测试:node --test 加 --experimental-test-coverage 就能出覆盖率。

面试官追问

追问 1mocha 全绿,覆盖率 100%,能说明代码没问题吗?
参考回答

不能。覆盖率只统计代码有没有被执行,一个没有断言的用例也能把覆盖率刷满。要看用例里有没有真正断言返回值和异常。

追问 2异步用例忘了 return,会怎样?
参考回答

mocha 认为函数执行完就通过了,Promise 里的断言失败根本没人接,用例照样绿。用 async 函数加 await,或者显式 return 那个 Promise。

追问 3同一个用例里既接了 done 参数又返回了 Promise,会怎样?
参考回答

新版 mocha 会直接报错 Resolution method is overspecified。两种方式选一种,现在基本都用 async/await,done 只留给纯回调风格的老代码。

追问 4测试本地通过,CI 上偶尔失败,最常见的原因是什么?
参考回答

用例之间共享了状态,或者依赖了真实时间、网络、随机数,执行顺序和速度一变就挂。时间用 sinon 的假时钟,网络用 nock 拦截,每个用例在 beforeEach 里重置状态。

# 《测试用例:mocha,should,istanbul》

# 目标

30 秒速记

  • 需求:fibonacci(0) = 0,fibonacci(1) = 1,fibonacci(n) = f(n-1) + f(n-2),fibonacci(10) = 55
  • 三种非法输入要抛错:n > 10、n < 0、n 不是数字
  • 测试目标:test/main.test.js 里行覆盖率和分支覆盖率都要 100%
  • 用例清单 = 0、1、10 三个正常值 + 11、-1、'abc' 三个异常值
  • 易漏:NaN 的 typeof 也是 number,小数也要想清楚接不接受

这个练习的目标是先把函数的规则写清楚,再用测试把每条规则都钉住,最后让行和分支覆盖率都到 100%。 fibonacci 的规则有六条:0 返回 0,1 返回 1,大于 1 走递归,再加上大于 10、小于 0、不是数字三种抛错。所以测试至少要六个用例,一条规则一个,光测一个 fibonacci(10) === 55 是远远不够的。我写这类测试的习惯是先把规则列成清单,每条对应一个 it,用例名直接写规则本身,以后哪条挂了一眼就知道是哪个需求被破坏了。

建立一个 lesson6 项目,在其中编写代码。

main.js: 其中有个 fibonacci 函数。fibonacci 的介绍见: http://en.wikipedia.org/wiki/Fibonacci_number 。

此函数的定义为 int fibonacci(int n)

  • 当 n === 0 时,返回 0;n === 1时,返回 1;
  • n > 1 时,返回 fibonacci(n) === fibonacci(n-1) + fibonacci(n-2),如 fibonacci(10) === 55;
  • n 不可大于10,否则抛错,因为 Node.js 的计算性能没那么强。
  • n 也不可小于 0,否则抛错,因为没意义。
  • n 不为数字时,抛错。

test/main.test.js: 对 main 函数进行测试,并使行覆盖率和分支覆盖率都达到 100%。

面试官追问

追问 1只断言了 fibonacci(10) === 55,覆盖率能到多少?
参考回答

分支覆盖率到不了 100%,三条抛错分支一次都没走。0、1 虽然会在递归里被执行到,但没有单独断言它们的返回值,基线写错了也不一定能第一时间发现。

追问 2传 NaN 进去会怎样?现有的 typeof n !== 'number' 拦得住吗?
参考回答

拦不住,typeof NaN 是 'number'。NaN 和什么比较都是 false,三个判断全部跳过,一直递归到 RangeError: Maximum call stack size exceeded。改成 if (!Number.isInteger(n)),NaN、小数、Infinity 一起挡掉。

追问 3上限从 10 改成可配置,测试怎么写才不会每次改配置都要改用例?
参考回答

用例围绕边界写:上限值本身能算、上限加一抛错,上限从配置里读。负数、非数字、基线这些和上限无关的用例保持不动。

追问 4上限放宽到 50,递归实现还能用吗?
参考回答

不能,朴素递归是指数级的,n = 50 要调用几百亿次。改成迭代或者加缓存,O(n) 就算完了;改实现时原来的测试正好用来回归。

# 知识点

30 秒速记

  • 测试框架 mocha:describe 分组、it 写用例、before/after 钩子,负责跑和报告结果
  • 断言库 should:(5).should.above(3) 这种链式写法,可以换成 chai 或 node:assert
  • 覆盖率工具 istanbul:统计语句、分支、函数、行四个维度,现在用 nyc 或 c8
  • Makefile:把长命令收成 make test、make cov;现在更常用 package.json 的 scripts

这节要掌握四样东西:mocha 跑测试、should 写断言、istanbul 看覆盖率、Makefile 把命令收起来。

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

测试用例 mocha,should,istanbul|Nodejs专题系列

# 测试用例 mocha,should,istanbul

30 秒速记

  • 三件套分工:mocha 跑用例,should 写断言,istanbul 统计覆盖率
  • 现在的选择:istanbul 命令行已停更,用 nyc 或 c8;Node 20 起也有内置的 node --test 和覆盖率
  • 测试要稳定可重复:时间、网络、数据库、随机数都隔离或 mock 掉
  • 覆盖率只说明「代码跑过」,不说明「结果对」,重点盯分支、异常和边界
  • 异步用例 return 一个 Promise 或用 async 函数,别和 done 混用

这三个工具各管一件事:mocha 负责组织和运行测试,should 负责写「结果应该是什么」的断言,istanbul 负责统计哪些代码被测到了。 这套组合比较老,现在我会这么选:运行器还是 mocha 或者换 vitest,断言用 Node 自带的 assert 或 chai,覆盖率用 nyc 或 c8,istanbul 的命令行早就不维护了,nyc 就是它的继任者。不管用哪套,思路是一样的:用例要覆盖正常值、边界和异常,覆盖率报告用来找漏测的分支,而不是追求一个好看的百分比。

// test/sum.test.js,运行:npx mocha
const assert = require('node:assert')
const sum = (a, b) => a + b
describe('sum', () => {
  it('1 + 2 = 3', () => assert.strictEqual(sum(1, 2), 3))
  it('async 用例直接 return / await', async () => {
    const v = await Promise.resolve(sum(2, 2))
    assert.strictEqual(v, 4)
  })
})
@前端进阶之旅: 代码已经复制到剪贴板

理解「测试用例 mocha,should,istanbul」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 testing 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

mocha + should + istanbul 是早期 Node 项目的经典测试组合,三者分工明确。

mocha 是测试运行器,提供 describe / it 组织用例,before / after / beforeEach / afterEach 做准备和清理,负责收集结果和退出码。它本身不带断言。

should 是断言库,它在 Object.prototype 上挂了一个 should 属性,所以能写成 (5).should.above(3)。缺点是 null 和 undefined 上没有这个属性,要改用 should(x).be.null()。同类的还有 chai(expect / should / assert 三种风格都有)和 Node 内置的 node:assert。

istanbul 是覆盖率工具,会统计四个维度:语句、分支、函数、行。它的命令行 istanbul cover 已经停止维护,现在用的是 nyc(同一个团队维护,基于 istanbul 的插桩库)或者 c8(直接用 V8 引擎内置的覆盖率数据,不用插桩)。

按今天的习惯,一个最小的配置可以这样写:

{
  "scripts": {
    "test": "mocha",
    "cov": "c8 mocha"
  },
  "devDependencies": { "mocha": "^10.0.0", "c8": "^10.0.0" }
}
@前端进阶之旅: 代码已经复制到剪贴板

npm run 会自动把 node_modules/.bin 加进 PATH,所以脚本里直接写 mocha 用的就是项目本地安装的版本,不需要全局安装,也不需要 Makefile。Node 20 以后还可以用零依赖的内置测试:node --test 加 --experimental-test-coverage 就能出覆盖率。

面试官追问

追问 1mocha 全绿,覆盖率 100%,能说明代码没问题吗?
参考回答

不能。覆盖率只统计代码有没有被执行,一个没有断言的用例也能把覆盖率刷满。要看用例里有没有真正断言返回值和异常。

追问 2异步用例忘了 return,会怎样?
参考回答

mocha 认为函数执行完就通过了,Promise 里的断言失败根本没人接,用例照样绿。用 async 函数加 await,或者显式 return 那个 Promise。

追问 3同一个用例里既接了 done 参数又返回了 Promise,会怎样?
参考回答

新版 mocha 会直接报错 Resolution method is overspecified。两种方式选一种,现在基本都用 async/await,done 只留给纯回调风格的老代码。

追问 4测试本地通过,CI 上偶尔失败,最常见的原因是什么?
参考回答

用例之间共享了状态,或者依赖了真实时间、网络、随机数,执行顺序和速度一变就挂。时间用 sinon 的假时钟,网络用 nock 拦截,每个用例在 beforeEach 里重置状态。

# 《测试用例:mocha,should,istanbul》

# 目标

30 秒速记

  • 需求:fibonacci(0) = 0,fibonacci(1) = 1,fibonacci(n) = f(n-1) + f(n-2),fibonacci(10) = 55
  • 三种非法输入要抛错:n > 10、n < 0、n 不是数字
  • 测试目标:test/main.test.js 里行覆盖率和分支覆盖率都要 100%
  • 用例清单 = 0、1、10 三个正常值 + 11、-1、'abc' 三个异常值
  • 易漏:NaN 的 typeof 也是 number,小数也要想清楚接不接受

这个练习的目标是先把函数的规则写清楚,再用测试把每条规则都钉住,最后让行和分支覆盖率都到 100%。 fibonacci 的规则有六条:0 返回 0,1 返回 1,大于 1 走递归,再加上大于 10、小于 0、不是数字三种抛错。所以测试至少要六个用例,一条规则一个,光测一个 fibonacci(10) === 55 是远远不够的。我写这类测试的习惯是先把规则列成清单,每条对应一个 it,用例名直接写规则本身,以后哪条挂了一眼就知道是哪个需求被破坏了。

建立一个 lesson6 项目,在其中编写代码。

main.js: 其中有个 fibonacci 函数。fibonacci 的介绍见: http://en.wikipedia.org/wiki/Fibonacci_number 。

此函数的定义为 int fibonacci(int n)

  • 当 n === 0 时,返回 0;n === 1时,返回 1;
  • n > 1 时,返回 fibonacci(n) === fibonacci(n-1) + fibonacci(n-2),如 fibonacci(10) === 55;
  • n 不可大于10,否则抛错,因为 Node.js 的计算性能没那么强。
  • n 也不可小于 0,否则抛错,因为没意义。
  • n 不为数字时,抛错。

test/main.test.js: 对 main 函数进行测试,并使行覆盖率和分支覆盖率都达到 100%。

面试官追问

追问 1只断言了 fibonacci(10) === 55,覆盖率能到多少?
参考回答

分支覆盖率到不了 100%,三条抛错分支一次都没走。0、1 虽然会在递归里被执行到,但没有单独断言它们的返回值,基线写错了也不一定能第一时间发现。

追问 2传 NaN 进去会怎样?现有的 typeof n !== 'number' 拦得住吗?
参考回答

拦不住,typeof NaN 是 'number'。NaN 和什么比较都是 false,三个判断全部跳过,一直递归到 RangeError: Maximum call stack size exceeded。改成 if (!Number.isInteger(n)),NaN、小数、Infinity 一起挡掉。

追问 3上限从 10 改成可配置,测试怎么写才不会每次改配置都要改用例?
参考回答

用例围绕边界写:上限值本身能算、上限加一抛错,上限从配置里读。负数、非数字、基线这些和上限无关的用例保持不动。

追问 4上限放宽到 50,递归实现还能用吗?
参考回答

不能,朴素递归是指数级的,n = 50 要调用几百亿次。改成迭代或者加缓存,O(n) 就算完了;改实现时原来的测试正好用来回归。

# 知识点

30 秒速记

  • 测试框架 mocha:describe 分组、it 写用例、before/after 钩子,负责跑和报告结果
  • 断言库 should:(5).should.above(3) 这种链式写法,可以换成 chai 或 node:assert
  • 覆盖率工具 istanbul:统计语句、分支、函数、行四个维度,现在用 nyc 或 c8
  • Makefile:把长命令收成 make test、make cov;现在更常用 package.json 的 scripts

这节要掌握四样东西:mocha 跑测试、should 写断言、istanbul 看覆盖率、Makefile 把命令收起来。 它们是分层的:运行器决定怎么组织和执行用例,断言库决定怎么判断对错,覆盖率工具告诉你哪些代码没测到,任务入口让所有人和 CI 都用同一条命令。分层的好处是每一层都能单独换,比如断言从 should 换成 chai,用例结构和运行方式都不用动。今天的项目里 Makefile 基本被 npm scripts 取代了,istanbul 也换成了 nyc 或 c8,但这个分工思路没变。

// should 风格
const should = require('should')
;(5).should.above(3)
'foobar'.should.startWith('foo')
should(null).be.null() // null 上没有 .should 属性,要这样写
@前端进阶之旅: 代码已经复制到剪贴板
  1. 学习使用测试框架 mocha : http://mochajs.org/
  2. 学习使用断言库 should : https://github.com/tj/should.js
  3. 学习使用测试率覆盖工具 istanbul : https://github.com/gotwarlost/istanbul
  4. 简单 Makefile 的编写 : http://blog.csdn.net/haoel/article/details/2886

← 服务端字符编解码&乱码处理 charset_enc_dec调试日志打印debug模块 →

fe