Web 前端的单元测试
简单地介绍前端的单元测试,以下内容,大部分来自网络,这次的内容,只是一次归纳而已
单元测试
什么是单元测试?
在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法
— 维基百科
需要访问数据库的测试不是单元测试
需要访问网络的测试不是单元测试
需要访问文件系统的测试不是单元测试
--- 修改代码的艺术
自动的、可重复的
容易实现
一旦写好,将来都可使用
任何人都可运行
可以快速运行
— 单元测试艺术
单元测试是一段自动化的代码,用来调用被测试的方法或类,而后验证基于该方法或类的逻辑行为的一些假设。单元测试几乎总是用单元测试框架来写的。它是全自动、可信赖、可读性强、可维护的。
— 单元测试艺术
为什么前端要做单元测试
- 自动化测试,能保证一定的正确性
- 方便重构代码
- 容易测试的代码,说明这可能是一个好的设计,或者便于维护和理解
- 可以快速验证你的代码的正确性
- 写单元测试,相当于写了一份如何使用该代码的说明
- 驱动开发(TDD)
开发模式
TDD
TDD(Test-driven development) - 测试驱动开发
我们平时的开发模式是:
- 需求分析
- 开始开发
- 转测
- 改 bug
- 测试
- 上线
TDD 整个流程大概是这样的:
- 需求分析
- 根据会出现的功能,是方法、类还是组件等,编写相关的测试用例
- 快速简单实现功能,使测试用例通过
- 对代码进行重构
- 测试用例通过
- 转测
- ….
通过测试驱动开发,有一些优势
- 先编写测试用例,其实就是先写出怎么使用该“单元”的事例代码
- 然后针对一些边界值、边界情况,尽可能覆盖全面
- 根据单元测试运行情况,先快速编写功能代码,达到测试用例都通过为止
- 然后开始重构你的代码,使代码更健壮、容易理解上手
多了一层单元测试,对我们的代码是多了一层保障
BDD
BDD(Behavior-driven development) - 行为驱动开发
BDD的核心价值是体现在正确的对系统行为进行设计,所以它并非一种行之有效的测试方法。它强调的是系统最终的实现与用户期望的行为是一致的、验证代码实现是否符合设计目标。但是它本身并不强调对系统功能、性能以及边界值等的健全性做保证,无法像完整的测试一样发现系统的各种问题。但BDD倡导的用简洁的自然语言描述系统行为的理念,可以明确的根据设计产生测试,并保障测试用例的质量。
我也不是很了解 BDD,有兴趣的自己找资料看看~
覆盖率
单元测试代码覆盖率,就是你的测试代码覆盖到功能代码的一个比例
当你的覆盖率很低,那肯定你的测试代码并没有很好测试到某个区域
当你的覆盖率很高或者 100%,证明你的模块所有的代码都是被测试过,但也只是测试过,而不能证明是所有情况都被测试到
所以这个测试覆盖率,我觉得可以当做成一个测试工具,让你清晰得看到哪些代码是没有测试到,而不能当做一个指标
总结
- 优点
- 保证代码一定的正确性,减少 bug 数
- 方便重构、维护
- 缺点
- 在前期,会需要更多开发时间
- 你写的单元测试代码不一定就是好的
讨论
- 前端的业务代码,是否需要做单元测试?
如何为 js、vue 搭建、编写单元测试
下面的测试介绍,使用的是
Jest
,需要Mocha
的话自行搜索
搭建环境
Jest
、Mocha
配置 Webpack
,自行搜索;现在是用 vue-cli3
的模板配置
什么是单元测试框架和断言库
单元测试框架,就是一个工具,提供一些方法、环境等,方便编写单元测试
断言,就是判断传入的值,是否和预期结果一致,断言库会提供各种各样的断言方法,方便你的测试;例如:断言这个值是否为一个对象、断言两个对象的属性是否一致等
expect(1).toBe(1) // true
expect(1).toBe('1') // false
Jest
断言
it('test', async () => {
expect('123').toBe('123') // true
})
异步测试
回调、promise
等处理,查看官方文档就好了,这里就说下 async/await
it('test', async () => {
const data = await func()
expect(data).toBe(true)
})
这样就行了,无需再做什么处理
测试请求
request
就是后台用的那个方法,nock
是用来模拟请求的
import { request } from '@packages/commonMethods';
import nock from 'nock';
const requestTemplate = ({ status, data } = {}) => ({
status: status || 200,
data: data || {},
code: '00000000'
});
describe('commonMethods - request', () => {
const url = 'http://localhost';
it.only('test', async () => {
// 模拟
nock(url)
.get('/api/test')
.reply(200, requestTemplate());
const { code, data } = await request({
url: url + '/api/test',
method: 'get'
});
expect(code).toBe('00000000'); // true
expect(data).toMatchObject({}); // true
});
});