系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
提示:以下是本篇文章正文内容,下面案例可供参考
一、前端要学的测试课
1.前端要学的测试
a.Jest入门;
b.TDD实战;
c.BDD实战;
2.前端工程化的一部分
a.前端自动化测试;
b.高质量代码设计;
c.高质量代码实现;
3.前端自动化测试的例子
Vue、Echarts、React、Ant-Design...这些都有使用到自动化测试。
4.前端为什么需要自动化测试?
a.改需求十,代码重构:导致修复时间长,成本高;
b.自动化测试,修复时间少,难度低;
5.课程涵盖内容
学习安排:
a. 第一章 课程介绍
b. 第二章 Jest基础
c. 第三章 Jest进阶
d. 第四&六 React 测试
e. 第五&七章 Vue测试
f. 第八章 前端自动化测试的思考总结
6.前置技能
7.学习收获
a. 彻底入门前端自动化测试
b. 根据项目完成测试方案选型
c. 主流前端测试工具使用
d. 完成前端系动画测试项目落地
e. 形成多维度前端架构思维
二、Jest前端自动化测试框架基础入门
1.自动化测试背景及原理
前端自动化测试产生的背景及原理
作为一名前端开发者,每天都会遇到各种各样的bug,比如安全性的bug,逻辑bug,性能bug,展示bug等,在日常开发过程种想要不出bug几乎是不可能的。
每当遇到复杂的业务场景或对代码进行修补的时候出现bug其实是非常正常的事情,bug本身并不可怕,可怕的是把bug真正的带到线上。
所以为了了防止bug上线,可以做些比如codeview的整合,通过测试同学的测试,帮助发现代码潜在的问题,或者通过灰度发布这样的机制帮助在代码上线之前进行局部的验证,这些方法都可以很好的帮助降低bug上线的概率。
但是对于前端来说还有没有更好的办法降低代码种bug出现的频率呢?是有的,一些简单错误可以通过以下几种工具来规避:
TypeScript、Flow、EsLint、StyleLint...
他们都可以帮助提高前端代码质量,减少bug数量,当然这还不够,还可以使用前端自动化测试工具来进一步避免bug的产生。
说到自动化测试,在后端自动化测试已经很普遍被应用了,当时在前端领域目前被普及的情况并不是很好,隐藏学习前端测试刻不容缓
常见测试种类:
a. 单元测试
b. 集成测试
c. end To end 端到端测试
d. 回归测试
e. 性能测试
f. 压力测试
为了更好的理解,接下来进入代码环节
a.新建文件夹(mkdir lesson1)
b.打开新建的文件夹(cd lesson1)
c.创建文件(touch math.js)
function add(a,b){
return a +b;
}
function minus(a,b){
return a - b;
}
创建文件(touch index.html), 引入math.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>math.js</title>
<script src="math.js"></script>
</head>
<body></body>
</html>
创建文件(touch math.test.js)
let result = add(3,7);
let expected = 10;
if(result !== expected){
throw Error(`3+7应该等于${expected},结果却是${result}`)
}
result = minus(3,3)
expected = 0;
if(result !== expected){
throw Error(`3-3应等于${expected},结果却是${result}`)
}
直接打开页面在浏览器的控制台,输入math.test.js中的测试代码并执行
a.index.html已经引入了math.js这个函数库,可以通过直接在控制台执行的方式调用
b.测试过程中若是修改了源代码,需要刷新页面再执行代码
我们写的测试的例子基本上都是一个套路,就是先预期一个结果,然后在执行计算出真正的结果,然后两个结果进行比较是否相同,如果相同就可以通过,如果不相同就抛出异常错误信息。
优化测试代码,单独封装一个expect函数,方便使用:
function expect(result){
return {
toBe:function(actual){
if(result !== actual){
throw Error('预期值和实际值不相等')
}
}
}
}
expect(add(3,3)).toBe(6);
expect(minus(6,3)).toBe(3);
2.前端自动化测试框架
在实际项目中只有except和test两个方法显然是不够的,同时还有很多自动化测试机制需要集成。
现在业界已经有很多前端自动化测试框架,一些框架里面集成了非常多的方法和机制供选用,在使用过程中的可以方便快捷的进行项目级别的前端自动化测试了。
目前业界主流的几个前端自动化测试框架包括 Jasmine、Mocha+Chai、Jest。
一个好的前端自动化测试框架应该在以下几个方面比较突出:
a.性能好
b.功能丰富
c.易用性高
Jest优势:
a.速度快(在编辑过程中可以自动运行修改部分的测试代码)
b. API简单
c.易配置
e.隔离性好
f.监控模式
g.IDE整合
h.快照Snapshot
i.多项目并行
j.覆盖率报告快速生成
k.Mock丰富
m.支持扩展性强,如:Babel、TypeScript、Node.js、React、angular、Vue
3.使用Jest修改自动化测试样例
接下来开始使用Jest,在这之前需要提前安装好Node环境
1.新建文件夹(mkdir lesson2)
2.进入新建的目录(cd lesson2)
3.初始化npm包管理环境(npm init)
4.一路回车后可以看到目录下生成一个文件———package.json
5.安装Jest(npm i jest@24.8.0 -D)
*-D表示只有在开发的时候才会运行测试用例
6.安装好后将上节代码复制过来并作如下修
math.js作为node模块到处
function add(a,b){
return a+b;
}
function minus(a,b) {
return a -b;
}
function multi(a,b){
return a*b;
}
module.exports = {
add,
minus,
multi
}
math.test.js中math.js作为node模块导入并使用
const math = require('./math');
const {
add,minus,multi
} = math;
test('测试加法3+7',()=>{
expect(add(3,3)).toBe(6)
})
test('测试减法6-3',()=>{
expect(minus(6,3)).toBe(3)
})
test('测试乘法3*3',()=>{
expect(multi(3,3)).tiBe(9)
})
修改配置package.json
{
...
"script":{
"test":"jest --watchALL"
},
...
}
--watchAll表示监听所有测试用例,当有发生变化时,自动运行jest重跑所有测试用例
控制台运行npm run test 结果如下:
>lesson2@1.0.0 test
> jest
PASS ./math.test.js
测试加法3+7 (4ms)
测试减法 6-3
测试乘法 3*3(1ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 5.468s
Ran all test suites.
若是测试代码写错,结果如下:
>lesson@1.0.0 test
>jest
FAIL ./math.test.js
√ 测试加法 3 + 7 (6ms)
√ 测试减法 6 - 3 (1ms)
× 测试乘法 3 * 3 (7ms)
● 测试乘法 3 * 3
exprct(received).toBe(expected) //Object.is equality
Expected: 3
Received:9
15 |
16 | test('测试乘法 3 * 3', () => {
> 17 | expect(multi(3,3)).toBe(3);
| ^
18 | });
at Object.<anonymous> (math.test.js:17:22)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 2 passed, 3 total
Snapshots: 0 total
Time: 4.258s
Ran all test suites.
为什么math中的方法必须导出来呢?
因为Jest在前端项目中帮助外面完成两类内容
1.单元测试(模块测试)
2.集成测试(多个模块测试)
因此必须要以模块的方式来使用
不论时按照CommonJS还是Es Module 改造,都需要符合Jest才能进行自动化测试
但是这样的花在html文件中使用会产生报错:
Uncaught ReferenceError: module is not defined
可以小改造以下(math.js)
...
try{
module.exports={
add,minus,multi
}
} catch (a) {}
这种报错一般是不会出现在项目中的,现在的项目基本都是模块花编码
4.Jest的简单配置
jest有默认配置,下面来自定义配置,在这之前需要生成配置文件,运行npx jest --init
// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// Respect "browser" field in package.json when resolving modules
// browser: false,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "C:\\Users\\AImooc-Oliver\\AppData\\Local\\Temp\\jest",
// Automatically clear mock calls and instances between every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: null,
// The directory where Jest should output its coverage files
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: null,
// A path to a custom dependency extractor
// dependencyExtractor: null,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: null,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: null,
// A set of global variables that need to be available in all test environments
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "json",
// "jsx",
// "ts",
// "tsx",
// "node"
// ],
// A map from regular expressions to module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
// preset: null,
// Run tests from one or more projects
// projects: null,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: null,
// Automatically restore mock state between every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: null,
// A list of paths to directories that Jest should use to search for files in
// roots: [
// "<rootDir>"
// ],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
// testEnvironment: "jest-environment-jsdom",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
// testMatch: [
// "**/__tests__/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: null,
// This option allows use of a custom test runner
// testRunner: "jasmine2",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
// transform: null,
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: null,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};
常见npx jest --coverage可以查看覆盖率
PASS ./math.test.js
√ 测试加法 3 + 7 (13ms)
√ 测试减法 6 - 3 (1ms)
√ 测试乘法 3 * 3 (1ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
math.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 4.569s
Ran all test suites.
不仅在控制台有,在根目录也生成了相应文件:
.
|-- clover.xml
|-- coverage-final.json
|-- lcov-report
| |-- base.css
| |-- block-navigation.js
| |-- index.html
| |-- math.js.html
| |-- prettify.css
| |-- prettify.js
| |-- sort-arrow-sprite.png
| `-- sorter.js
`-- lcov.info
1 directory, 11 files
Jest/lesson2/coverage/lcov-report/index.html
可以直接访问:
可以修改配置package.json
{
...
"script":{
"coverage":"jest --coverage"
}
}
之后运行 npm run coverage
,也是一样的效果
修改
Jest\lesson2\jest.config.js
中的coverageDirectory
可以指定生成目录名称
一般前端项目中都是使用 ESModule 的语法,按 ESModule 改一下:
math.js
function add(a,b) {
return a + b;
}
function minus(a,b) {
return a - b;
}
function multi(a,b) {
return a * b;
}
export {
add,
minus,
multi
}
math.test.js
import {
add,
minus,
multi
} from './math'
test('测试加法 3 + 7', () => {
expect(add(3,3)).toBe(6);
});
test('测试减法 6 - 3', () => {
expect(minus(6,3)).toBe(3);
});
test('测试乘法 3 * 3', () => {
expect(multi(3,3)).toBe(9);
});
改完代码之后,运行 jest 会有报错产生,这是因为 jest 是运行在 node 环境,并不能直接识别 ESModule 的语法,这就要用到 babel 了
安装babel相关依赖
npm i @babel.core@7.4.5 @babel/preset-env@7.4.5 -D
在根目录新建babel配置文件.babelrc
{
"presets":[
[
"@babel/preset-evn",{
"targets":{
"node":"current"
}
}
]
]
}
再次运行 jest ,成功!