单元测试的目的是,将你的项目划分成小单元,每个单元测试中尽量设计 case 将代码逻辑中的每个分支都运行到。这样所有的单元测试跑下来,项目中的每行代码最好都被跑过一次,即覆盖率尽量去靠近 100%。
很多项目会在 README 上放一个测试覆盖率的 badge,比如:
工具:istanbul(JavaScript 代码覆盖率工具)
代码覆盖率四个维度
测试的时候,我们常常关心,是否所有代码都测试到了。这个指标就叫做“代码覆盖率”(code coverage),它有四个测量维度。
- 行覆盖率(
line coverage):是否每一行都执行了? - 函数覆盖率(
function coverage):是否每个函数都调用了? - 分支覆盖率(
branch coverage):是否每个 if 代码块都执行了? - 语句覆盖率(
statement coverage):是否每个语句都执行了?
行(Lines of Source Code) vs 可执行代码(Lines of Executable Code)
“行覆盖率”中的行是指可执行代码行(Lines of Executable Code),而不是源文件中所有的行(含空行)——(Lines of Source Code)。
可执行代码行:
一般来说,包含语句的每一行都应被视为可执行行。而复合语句(简称为语句块,用{}括起来)会被忽略(但其内容除外)。
function doTheThing () // +0
{ // +0
const num = 1; // +1
console.log(num); // +1
} // +0
以下内容会被忽略(即视为非可执行行,+0)
1.非语句
一些覆盖率引擎会将以下两点视为可执行行,而 Istanbul 会忽略它们
- 该行只包含标点符号
:}、};、; - 定义时的方法(函数)名
2.import、声明
import、声明都被视为非可执行行(+0),require、赋值等语句视为可执行行(+1)
import { isEqual } from 'lodash'; // +0
const path = require('path'); // +1
require('jquery') // +1
let filePath // +0
const fileName = 'a.txt'; // +1 注:不仅是声明,还有赋值
class Person { // +0
constructor (name) { // +0
this.name = name; // +1
} // +0
static sayHello () { // +0
console.log('hello'); // +1
} // +0
walk () {} // +0
} // +0
function doTheThing () // +0
{ // +0
const num = 1; // +1
console.log(num); // +1
} // +0
如果某行存在可执行代码,则这一整行会被视为可执行代码行。 而如果一个语句被拆分为多行,则该可执行代码块中,仅第一行会被视为可执行行。
可执行代码行 vs 语句
一般情况下,如果我们遵守良好的代码规范,可执行代码行和语句的表现是一致的。然而当我们将两个语句放一行时,就会得到不同的结果。
// 2 lines、2 statements
const x = 1;console.log(x);
// 1 line、2 statements
const x = 1; console.log(x);
流程控制
JavaScript 的流程控制语句有:
- if
- white
- do … while
- switch
- … 运算符:
- 三目运算符(condition ? exprIfTrue : exprIfFalse) 我们需要确保流程控制的每个边界情况(即分支)都被执行(覆盖)。
测试覆盖率报告
上面代码中注释 //+1 的那些行,在覆盖率报告中,其左侧要么是绿色背景的 Nx,或者是粉红色背景的色块。因此我们可以通过颜色的不同,看出哪些是可执行代码行
绿色方框内的是 Lines of Source Code 行、红色框内粉红色色块是 Lines of Executable Code 可执行代码行。
其他标识
测试覆盖率报告出现的标识有:
- 'E':'else path not taken',表示 if/else 语句的 if(含 else if)分支已测试,而 else 分支未测试
- 'I':'if path not taken',与上面的 'E' 相反,即 if(含 else if)分支未测试
- 'Nx':表示当前可执行代码行被执行的总次数
- 粉色(背景色):语句/函数未覆盖
- 黄色(背景色):分支未覆盖
覆盖率数据到底有多大意义
- 覆盖率数据只能代表你测试过哪些代码,不能代表你是否测试好这些代码
- 不能盲目追求代码覆盖率,而应该想办法设计更多更好的案例,哪怕多设计出来的案例对覆盖率一点影响也没有
参考文章
Vue x Coveralls-辛辛苦苦写完测试也要秀一下
Istanbul 入门教程——阮一峰
看懂【测试覆盖率报告】
How do i read an Istanbul Coverage Report?