2024最新自动化测试 —— Jest 测试框架应用

什么是自动化测试

在软件测试中,自动化测试指的是使用独立于待测软件的其他软件来自动执行测试、比较实际结果与预期并生成测试报告这一过程。在测试流程已经确定后,测试自动化可以自动执行的一些重复但必要的测试工作。也可以完成手动测试几乎不可能完成的测试。对于持续交付和持续集成的开发方式而言,测试自动化是至关重要的。 ——来自 WiKi 百科

为什么要用前端自动化测试

随着前端项目的发展,其规模和功能日益增加。为了提高项目的稳定性和可靠性,除了需要测试工程师外,前端自动化测试也成为了不可或缺的一环。采用前端自动化测试可以有效地提高代码质量,降低出错的概率,从而使项目更加健壮和更易维护。

前端自动化分类和思想

单元测试

又称为模块测试 ,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。在前端中,一个函数、一个类、一个模块文件,都可以进行单元测试,测试时每个模块都是互不干扰的。

单元测试

集成测试

是在单元测试的基础上,测试在将所有的软件单元按照概要设计规格说明的要求组装成模块、子系统或系统的过程中各部分工作是否达到或实现相应技术指标及要求的活动。用户的开始操作到结束操作这一整个行为流程可以当做集成测试。

集成测试

TDD 测试驱动开发(Test Driven Development)

开发流程:

TDD

TDD 是趋向于白盒测试,需要开发者对当前编写的模块思路足够清晰。

优势:
  1. 长期减少回归 bug
  2. 代码质量更好,可维护性高。
  3. 测试覆盖率高(先写测试用例,再实现功能)。
  4. 错误测试代码不容易出现(测试在开发之前执行)。

BDD 行为驱动开发(Behavior Driven Development)

开发流程:

BDD

BDD 趋向于黑盒测试,只关注用户的一整套行为流程下来是否会成功。

优势:
  1. 对于用户行为的整个流程把控程度较高,对于开发人员来说这样安全感高。

如何自己写非框架测试用例

不使用测试框架,我们该如何测试自己的模块呢?如果我们想要测试下面的代码,应该需要两个值,一个是 期望值 ,另一个是函数执行的 结果值 ,我们需要对比两个值来进行判断当前函数是否通过了测试用例。

 

javascript

复制代码

// index.js function ZcyZooTeam(str) {  return 'Zcy' + str; }

需要下面的 if / else 进行判断当前的期望值 value 和结果值 result 是否相等,如果相等说明我们的测试用例通过了。我们将这两段代码复制到浏览器中,下面的执行不会通过,并会抛出错误,只有我们将传入值改为 ZooTeam 才会成功执行。

 

javascript

复制代码

// no-jest.js const result = ZcyZooTeam('Zero'); const value = 'ZooTeam'; if(result !== value) {  throw Error(`ZcyZooTeam 结果应为${value}, 但实际结果为${result}`); }

执行失败

是否能简化?

如果我们有多个函数需要测试,你应该不想写许多个 if / else 代码块吧?所以我们要将上面的代码块进行优化成一个函数。

 

javascript

复制代码

// no-jest.js function expect(result) {  return {    // 用于判断是否为期望值    toBe(value) {      if(result !== value) {        throw Error(`结果应为${value}, 但实际结果为${result}`);     }      console.log('测试通过!');   } } } ​ // 执行测试 expect(ZcyZooTeam('Zero')).toBe('ZcyZooTeam');

经过上面的封装,我们就可以只写一行代码进行测试了!

如何能清晰的看到我测的是哪个呢?

虽然上面的封装只需要书写一行代码就可以测试了,但是我们不知道执行结果和测试用例之间的对应关系,我们需要输出的文字来告诉我们当前是哪个测试用例执行了。

 

javascript

复制代码

// no-jest.js // 再封装如下方法 function test(msg, fn) {  try {    fn();    console.log(msg + '测试通过!'); } catch (error) {    console.log(msg + '测试未通过!' + error); } } ​ test('测试ZcyZooTeam', () => {  expect(ZcyZooTeam('Zero')).toBe('ZcyZooTeam') })

成功和失败都会进行提示,这样我们就可以知道当前是哪个测试用例成功/失败了。

Jest 的书写方式也是同上,如果上面的一整套代码了解了的话,你已经可以写 Jest 的测试脚本了,下面将进入 Jest 的配置。

如何使用 Jest 测试框架进行自动化测试?

主流的前端自动化测试框架

Jasmine

Jasmine 优点:易于学习和使用,支持异步测试,可以在浏览器和 Node.js 环境中运行,可以生成易于阅读的测试报告,可以与其他库和框架集成。

MOCHA

MOCHA 优点:支持异步测试和 Promise ,可以在浏览器和 Node.js 环境中运行,可以与其他库和框架集成,可以生成易于阅读的测试报告,可以使用各种插件和扩展来增强其功能。

Jest

Jest 是针对模块进行测试,单元测试对单个模块进行测试,集成测试对多个模块进行测试。

Jest 优点:速度快(单模块测试时,执行过的模块不会重复执行),API 简单,易配置,隔离性好(执行环境相对隔离,每个文件单独隔离互不干扰),监控模式(更灵活的运行各种测试用例),适配编辑器多,Snapshot (快照),多项目运行(后台前台测试用例并行测试),生成可视化覆盖率简单,Mock 丰富。

准备工作 —— Jest 的配置

 

scss

复制代码

npm i jest --save-D ​ // 初始化 jest 的配置文件 npx jest --init ​ // 你将在那个环境进行测试,回车即可选择 // 第一个是 node 环境、第二个是浏览器环境 ? Choose the test environment that will be used for testing › - Use arrow-keys. Return to submit.    node ❯   jsdom (browser-like) ​ // 是否需要 jest 生成测试覆盖率报告 ? Do you want Jest to add coverage reports? › (y/N) ​ // 是否需要在测试结束后清除模拟调用 ? Automatically clear mock calls and instances between every test? › (y/N) ​ // 创建 jest.config.js 文件 📝 Configuration file created at /Users/zcy1/Desktop/demo/auto-test-jest-demo/jest.config.js

以上方法执行结束后,会生成一个 jest.config.js 文件,里面包含了 Jest 的配置项,每个配置项都会带有描述,在初始化的两个配置也会体现在配置文件中

使用 babel 转换来使用 ES6 形式的导入和导出

 

json

复制代码

// .babelrc // 如果想用 es6 的形式导出,需要使用 babel 插件进行转换 // @babel/core @babel/preset-env ​ // 创建 .babelrc 文件 // 为了在 node 环境下使用 es6 的导出,需要使用 babel 进行转换 {  // 设置插件集合  "presets": [    // 使用当前插件,可以进行转换    // 数组的第二项为插件的配置项   [      "@babel/preset-env", {        // 根据 node 的版本号来结合插件对代码进行转换        "targets": {          "node": "current"       }     }   ] ] }

配置好后需要将 package.json 中的 test 命令的 value 改为 jest --watchAll ,代表监听所有有修改的测试文件,然后控制台执行 npm run test 就可以执行测试用例了。

Jest 启动时会进行如下流程
  1. npm run test
  2. jest (babel-jest) 检测当前环境是否安装了 babel
  3. 如果安装了则会去 babelrc 中取配置
  4. 取到后执行代码转换
  5. 最后再执行转化过的测试用例代码

如何生成一个测试用例覆盖率报告?

经过上面的 Jest 配置,我们就可以通过下面的 npx 命令来生成测试覆盖率报告了

 

css

复制代码

npx jest --coverage

会生成一个名为 coverage 的文件夹,打开里面的 html 就可以看到你的覆盖率,其中 Statements 是语句覆盖率(每个语句是否执行),Branches 是分支覆盖率(每个 if 块是否执行),Functions 是函数覆盖率(每个函数是否执行),Lines 是行覆盖率(每行是否执行),通过修改 coverageDirectory 的值可以改变测试覆盖率生成文件夹的名字

Jest 基础匹配器

上面我们说过了,Jest 的用法和我们封装的那几个函数是一样的,都是执行 test 函数并向函数中传递参数,第一个参数是你当前测试用例的描述,第二个参数是需要执行的匹配规则。

匹配器
toBe

toBe 匹配器,期待是否与匹配器中的值相等 相当于 object.is ===

 

scss

复制代码

// jest.test.js test("测试", () => {  expect(1).toBe(1);  // 通过  const a = { name: 'Zero' };  // 因为 a 的引用地址,和 toBe 中对象的引用地址不一致,会导致测试不通过,需要使用其他的匹配器  expect(a).toBe({ name: 'Zero' });  // 失败 });

toEqual

toEqual 匹配器,只会匹配对象中的内容是否相等。

 

scss

复制代码

// jest.test.js test('测试对象相等', () => { const a = { name: 'Zero' }; expect(a).toEqual({ name: 'Zero' }); // 断言 })

toBeNull

toBeNull 匹配器,可以判断变量是否为 null ,只能匹配 null。

 

scss

复制代码

// jest.test.js test('测试是否为null', () => { const a = null; expect(a).toBeNull(); })

toBeUndefined

toBeUndefined 匹配器,可以判断变量是否为 undefined ,只能匹配 undefined。

 

scss

复制代码

// jest.test.js test('测试是否为undefined', () => { const a = undefined; expect(a).toBeUndefined(); })

toBeDefined

toBeDefined 匹配器,希望被测试的值是定义好的。

 

scss

复制代码

// jest.test.js test('测试变量是否定义过', () => { const a = ''; expect(a).toBeDefined(); })

toBeTruthy

toBeTruthy 匹配器,可以判断变量是否为真值,会对非 bool 值进行转换。

 

scss

复制代码

// jest.test.js test('测试变量真值', () => { const a = '123'; expect(a).toBeTruthy(); })

toBeFalsy

toBeFalsy 匹配器,可以判断变量是否为假值,会对非 bool 值进行转换。

 

scss

复制代码

// jest.test.js test('测试变量假值', () => { const a = ''; expect(a).toBeFalsy(); })

not修饰符

not 匹配器,可以将匹配后的结果进行取反。

 

scss

复制代码

// jest.test.js test('测试变量不是假值', () => { const a = '1'; expect(a).not.toBeFalsy(); })

toBeGreaterThan

toBeGreaterThan 匹配器,期望值是否大于匹配器的参数。

 

scss

复制代码

// jest.test.js test('是否大于 a 的数字', () => { const a = 123; expect(a).toBeGreaterThan(1); })

toBeLessThan

toBeLessThan 匹配器,期望值是否小于匹配器的参数。

 

scss

复制代码

// jest.test.js test('是否小于 a 的数字', () => { const a = 0; expect(a).toBeLessThan(1); })

toBeGreaterThanOrEqual

toBeGreaterThanOrEqual 匹配器,期望值是否大于或等于匹配器的参数。

 

scss

复制代码

// jest.test.js test('是否大于等于 a 的数字', () => { // toBeLessOrEqual 匹配器,与之相反 const a = 123; expect(a).toBeGreaterThanOrEqual(1); })

toBeCloseTo

js 中,浮点数值在相加时不准确,使用 toBeCloseTo 匹配器解决,趋近于 0.3。

 

ini

复制代码

// jest.test.js test('是否大于等于 a 的数字', () => { const a1 = 0.1; const a2 = 0.2; expect(a1 + a2).toBeCloseTo(0.3); })

toMatch

toMatch 匹配器,匹配当前字符串中是否含有这个值,支持正则。

 

javascript

复制代码

// jest.test.js test('是否包含 day ', () => { const a = 'happy every day'; expect(a).toMatch('day'); })

toContain

toContain 匹配器,判断当前数组中是否包含这个元素,Set 也可以使用。

 

scss

复制代码

// jest.test.js test('数组中是否包含 zoo 这个元素', () => { const a = ['zoo', 'ZooTeam', 'Zero']; expect(a).toContain('zoo'); })

toThrow

toThrow 匹配器,可以捕捉抛出的异常,参数为抛出的 error ,可以用来判断是否为某个异常。

 

javascript

复制代码

// jest.test.js const error = () => { throw new Error('error'); } test('是否存在异常', () => { expect(error).toThrow(); })

以上就是 Jest 中比较基础的匹配器,可以结合 初始化 + 配置 + 基础匹配器 进行书写测试用例。

命令行操作

在运行 npm run test 命令的时候,控制台执行测试用例成功或失败后都会像下面的图片一样出现几行提示,让你按对应的键进行操作。

命令行

上面几个命令行的意思如下:
1. f 只会跑测试未通过的用例,再次点击 f 会取消当前模式。

我们使用一个失败的测试用例做一下示范

错误

失败

按下 f 后,Jest 只会执行刚才失败的测试用例

只测试失败

2. o 只监听已改变的文件,如果存在多个测试文件,可以开启,会与当前 git 仓库中的提交进行比较,需要使用 git 来监听哪个文件修改了,也可以将 --watchAll 改为 --watch 只会运行修改的文件。
3. 根据测试用例文件的正则表达式,过滤需要执行的测试用例文件,No tests found, exiting with code 0 如果填写不对会进行提示,并不会跑任何测试用例。
4. 根据测试用例描述的正则表达式,过滤需要执行的测试用例。
5. 退出测试用例监听。

异步测试

在正常的业务开发中,项目中不只有同步代码,还会有请求接口的异步代码,异步代码的测试与同步代码有稍许不同,我们来看一下。

编写一个接口请求
 

javascript

复制代码

// getData.js export const getData = (fn) => { axios.get('/getData').then((res) => { fn(res.data); }) }

对异步请求进行测试
 

scss

复制代码

// jest.test.js // 异步调用回调函数需要添加 done 参数,是一个函数 test('getData 返回结果为 { success: true }', (done) => { // 此处代码无效,因为测试用例不会等待请求结束后的回调,测试用例执行完就直接结束了 // getData1((data) => { // expect(data).toEqual({ // success: true // }) // }) getData1((data) => { expect(data).toEqual({ success: true }) // 需要在结束前调用 done 函数, Jest 会知道到 done 才会结束,才可以正确测试异步函数 done(); }) })

需要注意的是,如果传入了形参 done,但是没有使用,这个测试用例就会处于一直执行的状态,直到执行超时。

不执行done

还可以结合 promise 进行使用
 

kotlin

复制代码

// getData.js export const getData2 = () => { return axios.get('http://www.dell-lee.com/react/api/demo.json') } // jest.test.js test('getData 返回结果为 { success: true }', () => { // 使用 promise 时需要 return,在 then 中使用 done 也可以 return getData2().then(res => { expect(res.data).toEqual({ success: true }) }) }) // 测试请求是否 404 test('getData 返回结果为 404', () => { // 由于不触发 catch 就不会走测试校验,所以会成功,我们需要做一下限制 // 这行代码限制下面的代码中必须要执行一次 expect 方法,如果非 404 就不会走下面的 expect,则测试不会通过 expect.assertions(1); // 使用 promise 时需要 return // 如果只想测试 404 这样写是有问题的,需要配合 assertions 使用 return getData2().catch(err => { expect(err.toString().indexOf('404') > -1).toBe(true) }) }) // 另一种写法 test('getData 返回结果为 { success: true }', () => { // 会返回很多数据,其中包含 data 对象 // getData2().then((res) => console.log(res)) // { // status: 200, // statusText: 'OK', // headers: {}, // ...... // data: { success: true } // } // resolves 方法会将接口返回的字段全部获取,再使用 toMatchObject 方法进行匹配大对象中是否存在 data 对象 return expect(getData2()).resolves.toMatchObject({ data: { success: true } }) }) // 还可以使用 async/await test('getData 返回结果为 { success: true }', async () => { await expect(getData2()).resolves.toMatchObject({ data: { success: true } }) })

钩子函数

钩子函数可以当做一个测试用例的生命周期来看待,有 beforeAllbeforeEachafterEachafterAll

以下是一些关于钩子函数的概念和场景:

beforeAll:在所有测试用例执行前运行
beforeEach:在每个测试用例执行前执行一次
afterEach:在每个测试用例执行后执行一次
afterAll:在所有测试用例结束后运行

有时候,需要测试一个类中的多个方法,这些方法可能会反复操作同一个对象上的属性。如果使用同一个实例,就会相互干扰,导致测试用例无法通过。此时,需要使用不同的实例来进行测试。

Counter 类
 

javascript

复制代码

// Counter.js class Counter { constructor() { this.number = 0; } add() { this.number += 1; } minus() { this.number -= 1; } } export default Counter;

我们想要测试里面的 addminus 方法是否正确,需要实例化一个对象进行测试。但是下面的测试用例使用的永远都是同一个实例,第二个测试用例永远都不会通过。因为执行了第一个测试用例,第二个测试用例的值只能是 0。

 

scss

复制代码

// jest.test.js const count = new Counter(); // 使用下方两种测试方法会互相影响,先加一后减一,结果永远是 0 test('测试加法', () => { count.add(); expect(count.number).toBe(1); }) test('测试减法', () => { count.minus(); expect(count.number).toBe(-1); })

需要使用钩子函数,在每次执行测试用例的时候,都让他重新实例化一个对象
 

scss

复制代码

// jest.test.js let count = null; // 类似于生命周期 // 会在测试用例执行前运行 beforeAll(() => { console.log('beforeAll') }); // 会在每个测试用例执行前执行一次,这样就会解决上面互相影响的问题 beforeEach(() => { console.log('beforeEach') count = new Counter(); }); // 会在每个测试用例执行后执行一次 afterEach(() => { console.log('afterEach') }); // 会在所有测试用例结束后运行 afterAll(() => { console.log('afterAll'); }); test('测试加法', () => { console.log('add') count.add(); expect(count.number).toBe(1); }) test('测试减法', () => { console.log('minus') count.minus(); expect(count.number).toBe(-1); })

分组方法 discribe

 

scss

复制代码

// jest.test.js let count = null; // describe 方法,可以将测试用例进行分组,更加好维护同类型功能的测试用例 describe('count 测试', () => { beforeAll(() => { console.log('beforeAll') }); beforeEach(() => { console.log('beforeEach') count = new Counter(); }); afterEach(() => { console.log('afterEach') }); afterAll(() => { console.log('afterAll'); }); // 将 add 类型进行分组 describe('测试 add 类型用例', () => { // 在 describe 方法中,钩子函数会按照层级嵌套进行执行,先执行外部,再执行内部,不同的 describe 互不干扰 beforeEach(() => { console.log('beforeEach add'); }); test('测试加法', () => { console.log('add') count.add(); expect(count.number).toBe(1); }) }) // 将 minus 类型进行分组 describe('测试 minus 类型用例', () => { test('测试减法', () => { console.log('minus') count.minus(); expect(count.number).toBe(-1); }) }) })

加上 describe 方法的执行效果如下图。

分组

Mock

在日常开发中,当前端开发差不多后,后端接口可能还没有提供,这个时候我们就要用 Mock 数据。而 Jest 也有 Mock 方法,用于模拟一些 JavaScript 的函数等。

我们先来一个比较简单的 mock.fn
 

scss

复制代码

// mock.js export const runFn = (fn) => { fn(123); } // mock.test.js test('测试 runFn', () => { // 通过 jest 的 fn 方法创建一个模拟函数,如果不传参数会默认生成一个函数 // 1. 通过 func.mock 获取想要的值 // 2. 可以自定义返回值 // 3. 改变内部函数的实现,模拟接口请求,不请求代码中的接口 const func = jest.fn( () => 456 ); // 还可以使用 mockReturnValueOnce 方法进行控制输出,两种方法都使用时会覆盖 fn 方法中的返回值,支持链式调用 // 将 Once 去掉与 fn 方法一样,多次会返回相同的值 func.mockReturnValueOnce('zoo') // 返回 this 方法 mockReturnThis func.mockReturnThis(); // 还可以使用 mockImplementation 方法书写函数内部,可以在函数内部写逻辑,与 jest.fn 方法的参数一样,还可以填加 Once func.mockImplementation(() => { return '123'; }) // 执行被测函数 runFn(func); runFn(func); // console.log(func.mock) // 因为被调用了两次,所以长度都是 2 // { // calls: [ [123], [123] ], // 每次的调用情况,传递的参数是什么 // instances: [ undefined, undefined ], // 每次调用的 this 指向,被调用了几次 // invocationCallOrder: [ 1, 2 ], // 执行顺序,可能会传入同一个或多个方法中,需要记录一下顺序 // results: [ // mock 函数每次执行后的返回值 // { type: 'return', value: 456 }, // { type: 'return', value: 456 } // ] // } // 通过 toBeCalled 判断函数是否被调用 expect(func).toBeCalled(); // 判断当前函数调用了几次 被调用了两次 expect(func.mock.calls.length).toBe(2); // 判断参数是什么 expect(func.mock.calls[0]).toEqual([123]); // 判断每次调用的时候参数是什么 expect(func).toBeCalledWith(123); // 判断返回值 expect(func.mock.results[0].value).toBe('zoo'); })

Mock 高阶用法

如果需要通过修改请求的方式进行测试,而不使用测试框架,我们可能需要修改请求的代码逻辑。但是,Jest 提供了一种高级的 Mock 方法。我们只需在项目根目录下创建一个名为 __mocks__ 的文件夹,然后在其中自定义文件内容并导出,就可以使用自己定义的 Mock 函数而不必修改请求代码逻辑。

mock文件夹

mock函数

书写测试用例文件,引入 __mocks__ 文件夹中的函数
 

javascript

复制代码

// mocker.test.js // 使用 mock 方法引用 __mocks__ 下创建的 mock.js jest.mock("./mock"); // 执行完上面的方法,会直接寻找 __mocks__ 下的getData,而不是正常的请求文件 // 由于 mock 中没有 getCode 方法,最好只 mock 异步函数,同步函数直接测试即可 // 可以不必须创建 __mocks__ 文件夹 import { getData, } from "./mock"; // 需要使用下面的 requireActual 方法来引用非 mock 文件夹下的 getCode const { getData } = jest.requireActual("./mock"); // 高阶mock // 此处直接使用 __mocks__ 目录下的 mock 文件中的函数 test("测试 getData", () => { return getData().then((data) => { expect(eval(data)).toEqual("123"); }); });

Mock-timers

在特定的业务中,需要使用到定时器,测试的时候也是需要修改代码来测试不同时间,最主要的一点是,我们需要等时间才能看到我们的执行结果,Jest 也有关于定时器的 Mock 函数。

 

scss

复制代码

// mock.js export const timer = (fn) => { setTimeout(() => { fn(); setTimeout(() => { fn(); }, 3000) }, 3000) }

 

scss

复制代码

// mock-timers.test.js import { timer } from './mock'; // 使用 useFakeTimers 方法告知 Jest 在下面的测试用例,如果用到了定时器异步函数的时候,都是用假的 timers 进行模拟 jest.useFakeTimers(); test('测试 timer', () => { const fn = jest.fn(); timer(fn); // 使用 runAllTimers 方法,让定时器立即执行,和 useFakeTimers 配合使用 jest.runAllTimers(); // 如果代码中有多个定时器嵌套,只想测试最外层的定时器,则需要使用 runOnlyPendingTimers 方法 // 这个方法会只执行当前在队列中的函数,可以多次调用 jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers(); // advanceTimersByTime 方法,可以快进时间 // 因为 timer 中,三秒后只执行了第一层,如果是六秒,则会执行两次 fn jest.advanceTimersByTime(3000); })

snapshot 快照

到这里我们已经可以测试一些代码了,但是我们要如何捕捉执行结果和当前做对比呢?这时候就要使用快照功能了。

 

javascript

复制代码

// snapshot.js export const config1 = () => { return { method: 'GET', url: '/api', time: new Date() } } export const config2 = () => { return { method: 'GET', url: '/api', time: new Date().getTime() } }

 

scss

复制代码

// snapshot.test.js import { config1, config2 } from "./snapshot"; test('测试 config1 返回值', () => { // 但如果每次函数修改的时候,当前测试用例也要不断地修改 // expect(config()).toEqual({ // method: 'GET', // url: '/api' // }); // 需要使用快照匹配 toMatchSnapshot 方法 // 此方法会生成一个 __snapshots__ 目录,下面的文件中,第一次执行中 config 生成的结果会存到快照文件中 // 快照会根据 test 方法中的描述生成一个映射关系 // 修改后的 config 的执行结果与快照中的结果不同时会报错,需要更新快照 // 如果 config 中有的值是每次运行都会变化的,那么每次快照都不会与当前执行相同,除非执行后再更新快照 // 需要将在 toMatchSnapshot 方法中传递一个参数,设置一下 time 为任意格式的 Date 类型 expect(config1()).toMatchSnapshot({ time: expect.any(Date) }); }) test('测试 config2 返回值', () => { expect(config2()).toMatchSnapshot({ time: expect.any(Number) }); })

行内快照生成
 

scss

复制代码

// snapshot.test.js // 需要安装 prettier test("测试 config2 返回值", () => { // toMatchInlineSnapshot 方法,将执行快照放到行内中,会放到 toMatchInlineSnapshot 方法中 expect(config2()).toMatchInlineSnapshot( { time: expect.any(Number) }, ` Object { "method": "GET", "time": Any<Number>, "url": "/api", } ` ); });

对 dom 节点测试

Jest 内部自己模拟了一套 jsDom ,可以在 node 的环境下执行需要浏览器环境 dom 的测试用例。

 

javascript

复制代码

// dom.js import $ from 'jquery'; const addDiv = () => { // jQuery $('body').append('<div/>'); } export default addDiv; // dom.test.js import addDiv from './dom'; import $ from 'jquery'; test('测试 addDiv', () => { addDiv(); addDiv(); console.log($('body').find('div').length); // 测试dom expect($('body').find('div').length).toBe(2); expect(document.getElementsByTagName('div').length).toBe(2); })

VSCode 插件

Jest Snippets 用于快速生成 Jest 代码块的工具。

Jest 能够检测当前文件夹中的测试用例并自动运行测试,还支持可视化操作,更新、执行以及单个执行等功能,非常方便!

常用配置解读

 

java

复制代码

module.exports = { // 检测从哪个目录开始,rootDir 代表根目录 roots: ["<rootDir>/src"], // 代码测试覆盖率通过分析那些文件生成的,!代表不要分析 collectCoverageFrom: [ // src 下所有 js jsx ts tsx 后缀的文件 "src/**/*.{js,jsx,ts,tsx}", // src 下所有 .d.ts 后缀的文件 "!src/**/*.d.ts" ], // 运行测试之前,我们额外需要准备什么 setupFiles: ["react-app-polyfill/jsdom"], // 当测试环境建立好后,需要做其他事情时可以引入对应的文件 setupFilesAfterEnv: ["<rootDir>/src/setupTests.js"], // 哪些文件会被认为测试文件 testMatch: [ // src 下的所有 __tests__ 文件夹中的所有的 js jsx ts tsx 后缀的文件都会被认为是测试文件 "<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}", // scr 下的所有一 .test/spec.js/jsx/ts/tsx 后缀的文件都会被认为是测试文件 "<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}", ], // 测试运行的环境,会模拟 dom testEnvironment: "jsdom", // 测试文件中引用一下后缀结尾的文件会使用对应的处理方式 transform: { // 如果引用的是 js jsx mjs cjs ts tsx 后缀的文件会使用 /config/jest/babelTransform.js 文件进行处理 "^.+\.(js|jsx|mjs|cjs|ts|tsx)$": "<rootDir>/config/jest/babelTransform.js", // 如果引用的是 css 后缀的文件,会使用 /config/jest/cssTransform.js 文件处理 "^.+\.css$": "<rootDir>/config/jest/cssTransform.js", // 不是以 js jsx mjs cjs ts tsx css json 这些为后缀的文件会使用 /config/jest/fileTransform.js 文件进行处理 "^(?!.*\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js", }, // 忽略 transform 配置转化的文件 transformIgnorePatterns: [ // node_modules 目录下的 js jsx mjs cjs ts tsx 后缀的文件都不需要转化 "[/\\]node_modules[/\\].+\.(js|jsx|mjs|cjs|ts|tsx)$", // .module.css/sass/scss 后缀的文件都不需要转化 "^.+\.module\.(css|sass|scss)$", ], // 自动化测试时,应用的模块应该从哪里寻找,默认是在 node_modules modulePaths: [], // 模块名字使用哪种工具进行映射 moduleNameMapper: { // 针对于 native 移动端 // "^react-native$": "react-native-web", // 将 .module.css/sass/scss 模块使用 identity-obj-proxy 工具进行转化 "^.+\.module\.(css|sass|scss)$": "identity-obj-proxy", }, // 引入模块时,进行自动查找模块类型,逐个匹配 moduleFileExtensions: [ "web.js", "js", "web.ts", "ts", "web.tsx", "tsx", "json", "web.jsx", "jsx", "node", ], // 监听插件 watchPlugins: [ "jest-watch-typeahead/filename", "jest-watch-typeahead/testname", ], // 重置 mock resetMocks: true, };

小结

在实际业务应用中,我们建议对可复用的组件、工具函数、工具类等一些无副作用,可预知结果的代码来进行单元测试。在前期开发过程中的投入会大于没有单元测试的投入,因为要写一些测试用例,还要执行测试用例,优化代码等。但是在长久迭代中,这种方法会比没有进行单元测试的模块更加稳定。

代码地址

  1. 前置 demo :github.com/Jadony/Jest…
  2. Jest 简单配置:github.com/Jadony/jest…
  3. Jest 匹配器:github.com/Jadony/jest…
  4. 异步代码测试:github.com/Jadony/jest…
  5. Jest 钩子函数:github.com/Jadony/jest…
  6. Jestmock 函数:github.com/Jadony/jest…
  7. Jest 的快照:github.com/Jadony/jest…
  8. JestDom 节点的测试:github.com/Jadony/jest…

参考文档

  • 《前端要学的测试课 从Jest入门到TDD/BDD双实战》

如果想学习更多内容,请移步至 Jest 官方文档。

推荐阅读

瀑布流组件陷入商品重复怪圈?我是如何用心一解的!

初探Web客户端追踪技术

了解 ZRender 和 Echarts

JavaScript中的 this 指向

0基础实现项目自动化部署

开源作品

  • 政采云前端小报

开源地址 www.zoo.team/openweekly/ (小报官网首页有微信交流群)

  • 商品选择 sku 插件

开源地址 github.com/zcy-inc/sku…

招贤纳士

政采云技术团队(Zero),Base 杭州,一个富有激情和技术匠心精神的成长型团队。政采云前端团队(ZooTeam),一个年轻富有激情和创造力的前端团队。团队现有 80 余个前端小伙伴,平均年龄 27 岁,近 4 成是全栈工程师,妥妥的青年风暴团。成员构成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在日常的业务对接之外,还在物料体系、工程平台、搭建平台、智能化平台、性能体验、云端应用、数据分析、错误监控及可视化等方向进行技术探索和实战,推动并落地了一系列的内部技术产品,持续探索前端技术体系的新边界。

如果你想改变一直被事折腾,希望开始能折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊… 如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的前端团队的成长历程,我觉得我们该聊聊。任何时间,等着你写点什么

  总结

如果你对此文有任何疑问,如果你也需要接口项目实战,如果你对软件测试、接口测试、自动化测试、面试经验交流感兴趣欢迎加入我们,加入方式在文章的最后面

  自动化测试相关教程推荐:

2023最新自动化测试自学教程新手小白26天入门最详细教程,目前已有300多人通过学习这套教程入职大厂!!_哔哩哔哩_bilibili

2023最新合集Python自动化测试开发框架【全栈/实战/教程】合集精华,学完年薪40W+_哔哩哔哩_bilibili

测试开发相关教程推荐

2023全网最牛,字节测试开发大佬现场教学,从零开始教你成为年薪百万的测试开发工程师_哔哩哔哩_bilibili

postman/jmeter/fiddler测试工具类教程推荐

讲的最详细JMeter接口测试/接口自动化测试项目实战合集教程,学jmeter接口测试一套教程就够了!!_哔哩哔哩_bilibili

2023自学fiddler抓包,请一定要看完【如何1天学会fiddler抓包】的全网最详细视频教程!!_哔哩哔哩_bilibili

2023全网封神,B站讲的最详细的Postman接口测试实战教学,小白都能学会_哔哩哔哩_bilibili

  总结:

 光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

​​​

​​​

如果对你有帮助的话,点个赞收个藏,给作者一个鼓励。也方便你下次能够快速查找。

如有不懂还要咨询下方小卡片,博主也希望和志同道合的测试人员一起学习进步

在适当的年龄,选择适当的岗位,尽量去发挥好自己的优势。

我的自动化测试开发之路,一路走来都离不每个阶段的计划,因为自己喜欢规划和总结,

测试开发视频教程、学习笔记领取传送门!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/724520.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

xcode和iPhone真机或者watch真机连接问题

1.如果真机是第一次连接xocde&#xff0c;就需要开启真机上的开发者模式&#xff0c;开启开发者模式的方式&#xff1a; iphone/ipad开启方式: 设置 > 隐私与安全 > 开发者模式 > 开启&#xff0c;然后重启就可以了 watch设置&#xff1a;很麻烦&#xff0c;看文章…

对身外之物,不必在意

不管对待自己还是他人&#xff0c;外在的条件不值得挂怀&#xff0c;人在不得已颠沛流离时期&#xff0c;吃不好、穿不好&#xff0c;都应该从容接受。

外汇天眼:盈透证券为客户提供更丰富的欧洲衍生品交易渠道

电子交易巨头盈透证券&#xff08;纳斯达克代码&#xff1a;IBKR&#xff09;今日宣布&#xff0c;通过Cboe欧洲期权交易所&#xff08;CEDX&#xff09;新增欧洲股票期权和欧洲指数期货及期权。这一新增功能使得盈透证券的客户可以在单一统一平台上&#xff0c;除了股票、期权…

力扣469A

文章目录 1. 题目链接2. 题目代码3. 题目总结4. 代码分析 1. 题目链接 I Wanna Be the Guy 2. 题目代码 #include<iostream> #include<set> using namespace std; int main(){int highestLevelOfGame;cin >> highestLevelOfGame;set<int> levelCanPas…

【profinet】从站开发要点

目录 0、常见缩写及关键字注释 1、profinet简介 2、profinet协议栈 3、profinet数据帧 4、profinet网络解决方案示例 5、Application areas 注&#xff1a;本文主要简述profinet从站开发涉及到的知识点。【不足之处后续慢慢补充】。 0、常见缩写及关键字注释 MRP: Media…

移动硬盘接入mac无法复制文件进去怎么办,mac里的文件如何存进移动硬盘

如果要传输的文件数据量比较大&#xff0c;相比于使用U盘&#xff0c;移动硬盘是更多的选择。移动硬盘可存储量大、传输速度快&#xff0c;是实用性比较强的储存设备。不仅是Windows设备&#xff0c;Mac电脑也经常使用到移动硬盘。但有时候&#xff0c;移动硬盘在Mac上不能传文…

XHS-Downloader是一款小红书图片视频下载工具

这款软件可以提取账号发布、收藏、点赞作品链接&#xff1b;提取搜索结果作品链接、用户链接&#xff1b;下载小红书作品信息&#xff1b;提取小红书作品下载地址&#xff1b;下载小红书无水印作品文件&#xff01; &#x1f4d1; 功能清单 ✅ 采集小红书图文 / 视频作品信息…

【Pmac】PMAC QT联合开发中各种可能遇到的坑

目录 1. 错误 C2027 使用了未定义类型“PCOMMSERVERLib::DEVUPLOAD”2. 输入了正确的pmac的ip地址&#xff0c;没有显示可选的pmac设备3. Pmac DTC-28B无读数 使用QT编写PMAC上位机程序时&#xff0c;利用QT中的dump工具可以将pcommserver.exe转化为pcommserverlib.h和pcommser…

AMBA-CHI协议详解(四)

《AMBA 5 CHI Architecture Specification》 AMBA-CHI协议详解&#xff08;一&#xff09; AMBA-CHI协议详解&#xff08;二&#xff09; AMBA-CHI协议详解&#xff08;三&#xff09; AMBA-CHI协议详解&#xff08;四&#xff09; 文章目录 2.3.3 Atomic transactions2.3.4 S…

Hadoop3:MapReduce中的Shuffle机制

一、流程图 Shuffle是Map方法之后&#xff0c;Reduce方法之前的数据处理过程称。 二、图解说明 1、数据流向 map方法中context.write(outK, outV);开始&#xff0c;写入环形缓冲区&#xff0c;再进行分区排序&#xff0c;写到磁盘 reduce方法拉取磁盘上的数据&#xff0c;…

swift使用swift-protobuf协议通讯,使用指北

什么是Protobuf Protobuf&#xff08;Protocol Buffers&#xff09;协议&#x1f609; Protobuf 是一种由 Google 开发的二进制序列化格式和相关的技术&#xff0c;它用于高效地序列化和反序列化结构化数据&#xff0c;通常用于网络通信、数据存储等场景。 为什么要使用Proto…

【QT5】<重点> QT多线程

文章目录 前言 一、QThread创建多线程 二、QMutex基于互斥量的同步 三、QReadWriteLock线程同步 四、QWaitCondition线程同步 五、QSemaphore基于信号量的同步 前言 本篇记录学习QT多线程的知识&#xff0c;参考视频13.1QThread创建多线程程序_哔哩哔哩。若涉及版权问题…

Linux - 进程

一、什么是进程 首先&#xff0c;Linux是一个多用户多进程的操作系统&#xff0c;系统上可以同时运行多个进程。 进程的产生&#xff1a;①是在执行程序或者命令时产生的&#xff1b;②定时任务进程 进程的类型&#xff1a;前台进程/后台进程 前台进程&#xff1a;一个终端…

迭代器模式观察者模式

文章目录 1.引出迭代器模式1.展示院系结构2.传统方式 2.迭代器模式解决院系结构展示问题1.基本介绍2.原理类图3.类图4.代码实现1.Department.java 存储信息的对象2.College.java 被迭代的类型接口3.ComputerCollege.java 被迭代的具体实现类&#xff0c;存储数据并将其在创建迭…

契约锁电子签章平台 add 远程命令执行漏洞复现(XVE-2023-23720)

0x01 产品简介 契约锁电子签章平台是上海亘岩网络科技有限公司推出的一套数字签章解决方案。契约锁为中大型组织提供“数字身份、电子签章、印章管控以及数据存证服务”于一体的数字可信基础解决方案,可无缝集成各类系统,让其具有电子化签署的能力,实现组织全程数字化办公。通…

【LeetCode刷题】面试题 17.19. 消失的两个数字

1. 题目链接2. 题目描述3. 解题方法4. 代码 1. 题目链接 面试题 17.19. 消失的两个数字 2. 题目描述 3. 解题方法 例子假设&#xff1a; 数组A元素为 &#xff1a;1 &#xff0c;4&#xff0c;5 缺少的元素为&#xff1a;2&#xff0c; 3 那么所有整数就为1 ~ 5&#xff…

什么是无限铸币攻击?它是如何运作的?

一、无限铸币攻击解释 无限铸币攻击是指攻击者操纵合约代码不断铸造超出授权供应限制的新代币。 这种黑客行为在去中心化金融 (DeFi) 协议中最为常见。这种攻击通过创建无限数量的代币来损害加密货币或代币的完整性和价值。 例如&#xff0c;一名黑客利用了 Paid 网络的智能…

shadertoy-安装和使用

一、安装vscode 安装vscode流程 二、安装插件 1.安装glsl编辑插件 2.安装shader toy插件 三、创建glsl文件 test.glsl文件 float Grid(float size, vec2 fragCoord) {vec2 r fragCoord / size;vec2 grid abs(fract(r - 0.5) - 0.5) / fwidth(r);float line min(grid…

网络安全从入门到精通(特别篇I):应急响应案例

蓝队应急响应实战 1. 应急响应1. 应急响应 获取当前WEB环境的组成架构(语言,数据库,中间件,系统等) 分析思路: 1、利用时间节点筛选日志行为 2、利用已知对漏洞进行特征筛选 3、利用后门查杀进行筛选日志行为 #内容点: 应急响应: 1、抗拒绝服务攻击防范应对指南 2、勒…

【云原生】Kubernetes----证书过期处理办法

目录 引言 一、证书过期的问题与影响 二、解决方案 &#xff08;一&#xff09;查看证书剩余时间 &#xff08;二&#xff09;备份重要数据 &#xff08;三&#xff09;更新证书 &#xff08;四&#xff09;重启相关组件的pod 引言 随着云计算技术的飞速发展&#xff0…