前端自动化测试:测试到底在测什么?

在这里插入图片描述
对于稍微有一些开发经验的同学在开发过程中总会经历下面类似的问题:

  • 每次在版本发布上线之前,在电脑前蹲上好几个小时甚至是更长时间对你的应用进行测试,这个过程非常枯燥而痛苦。
  • 当代码的复杂度达到了一定的级别,当维护者的数量不止你一个,你应该会逐渐察觉到你在开发新功能或修复 bug
    的时候,会变得越发小心翼翼,即使代码看起来没什么问题,但你心里还是会犯嘀咕:这个 Feature 会不会带来其他 Bug ?这个 Fix
    会不会引入其他"Feature" ?
  • 当你想要对项目中的代码进行重构的时候,你会花费大量的时间进行回归测试。

以上这些问题都是由于大多数开发者所使用最基本的手动测试的方式所带来的问题,解决它的根本举措就是引入自动化测试方案。

测试的流程

在实际开发过程中,编写自动化测试代码通常是开发人员不太喜欢的一个环节。大多数情况下,前端开发人员在开发完一项功能后,只是打开浏览器手动点击,查看效果是否正确,之后就很少对该块代码进行管理。
造成这种情况的原因主要有两个:

  • 一个是业务繁忙,没有时间进行测试的编写。
  • 另一个是不知道如何编写测试。

但这些问题不应该作为我们掌握前端自动化测试的绊脚石。而且,一旦掌握了前端自动化测试方案,无论是对大型项目的开发,还是升职加薪,都是有益的。

提到测试的时候,即使是最简单的一个代码块可能都让初学者不知所措。最常问的问题的是“我怎么知道要测试什么?”。如果你正在写一个 Web 应用,那么你每个页面每个页面的测试用户交互的方式,就是一个很好的开端了。但 Web 应用也是由很多个函数和模块组成的代码单元,也是需要测试的。通常有两种情况:

  • 你接手的遗留代码没有写测试用例
  • 你必须从无到有的实现一个新功能

该怎么办呢?对于上面两种场景,你可以把测试视为代码的一部分来编写。我所说的这些代码,是用来检查给定的函数是否产生预期输出结果的。一个典型的测试流程如下:

  1. 引入要测试的函数

  2. 给函数一个输入

  3. 定义预期输出

  4. 检查函数是否返回了预期的输出结果

就这么多。这样看测试也没那么可怕的嘛:输入 —— 预期输出 —— 验证结果。

一个测试案例

下面来看一个例子:

// math.js
functionadd (a, b) {
  return a + b
}​

functionsubtract (x, y) {
  return x - y
}​

module.exports= {
  add,
  subtract
}

如何保证上面代码的正确性?

下面来写一段测试代码:

// test.jsconst { add, subtract } =require('./math')const result =add(1,2)
const expected =3if (result !== expected) {
  thrownewError(`1 + 2 应该等于${expected},但是结果却是${result}`)
}const result2 =subtract(2,1)
const expected2 =1if (result2 !== expected2) {
  thrownewError(`2 - 1 应该等于${expected2},但是结果却是${result2}`)
}

命令行执行 node test.js 后,会看到错误信息:

Error: 1 + 2 应该等于 3,但是结果却是 2

通过测试代码可以很方便的帮助验证代码的正确性。

封装测试工具函数

之前示例的测试代码太过繁琐,可以思考一下能否封装的更简便一些,比如下面这样:

expect(add(1,2)).toBe(3)
expect(subtract(2,1)).toBe(-1)

上面的测试代码就像自然语言说话一样,很舒服。

实现 expect 方法:

// test.js
const { add, subtract } =require('./math')expect(add(1,2)).toBe(3)
expect(subtract(2,1)).toBe(1)​

functionexpect (result) {
  return {
    toBe (actual) {
      if (result !== actual) {
        thrownewError(`预期值和实际值不相等,预期结果: ${actual},实际结果: ${result}`)
      }
    }
  }
}

增加错误提示信息:

// test.js
const { add, subtract } =require('./math')test('测试加法', () => {
  expect(add(1,2)).toBe(3)
})test('测试减法', () => {
  expect(subtract(2,1)).toBe(1)
})

​functiontest (description, callback) {
  try {
    callback()
    console.log(`${description}通过测试`)
  } catch (err) {
    console.error(`${description}没有通过测试:${err}`)
  }
}​

functionexpect (result) {
  return {
    toBe (actual) {
      if (result !== actual) {
        thrownewError(`预期值和实际值不相等,预期结果: ${actual},实际结果: ${result}`)
      }
    }
  }
}

Jest 测试框架应用

​Jest : jestjs.io/zh-Hans/是 Facebook 出品的一个 JavaScript 开源测试框架。

相对其他测试框架,其一大特点就是就是内置了常用的测试工具,比如零配置、自带断言、测试覆盖率工具等功能,实现了开箱即用。

Jest 适用但不局限于使用以下技术的项目:Babel、TypeScript、 Node、 React、Angular、Vue 等。

Jest 主要特点:

零配置

自带断言

作为一个面向前端的测试框架, Jest 可以利用其特有的快照测试功能,通过比对UI 代码生成的快照文件,实现对 React 等常见前端框架的自动测试。

Jest 的测试用例是并行执行的,而且只执行发生改变的文件所对应的测试,提升了测试速度。

测试覆盖率

Mock 模拟

快速体验 Jest

安装 Jest 到项目中:

npm install --save-dev jest



//math.js
functionadd (a, b) {
  return a * b
}

​functionsubtract (x, y) {
  return x - y
}​

module.exports= {
  add,
  subtract
}



//test.js ==> math.test.js
const { add, subtract } =require('./math')test('测试加法', () => {
  expect(add(1,2)).toBe(3)
})test('测试减法', () => {
  expect(subtract(2,1)).toBe(1)
})



//package.json
{
  "scripts":{
    "test":"jest"
  }
}

jest 命令会运行项目中所有以 .test.js 结尾的文件

最后运行测试命令:

npm run test

解析:

jest 找到项目中所有以 .test.js 结尾的文件并运行

jest 会给测试文件提供 test、expect 等全局函数,所以在测试文件中可以直接使用

jest 为测试结果提供了良好的日志输出

解决 vscode 中 jest 代码提示问题

npm i -D @types/jest

注意:@types/jest 必须安装到项目的根目录,并且以根目录的方式在 vscode 中打开,否则不生效。或者说只要是 vscode 打开的项目根目录有 @types/jest 这个包就可以了。

配置文件

npx jest--init

配置文件生成选项:

是否使用 ts ;
使用哪种测试环境;
使用 jest 收集测试覆盖率报告;
使用那种引擎检测覆盖率:v8 处于实验性阶段,建议 Node v14 后使用,babel 较为成熟
每次测试时,是否自动清除 mock 实例;

详细配置信息参考:jestjs.io/docs/zh-Han…

//jest.config.js
/*
 * For a detailedexplanation regarding each configuration property, visit:
 *https://jestjs.io/docs/en/configuration.html
 */​

module.exports= {
  // 自动 mock 所有导入的外部模块
  // automock: false,// 在指定次数失败后停止运行测试
  // bail: 0,​

  // The directory where Jest should store its cached dependencyinformation
  // cacheDirectory:"/private/var/folders/5h/_98rffpj1z95b_0dm76lvzm40000gn/T/jest_dx",​

  // 在每个测试之间自动清除 mock 调用和实例
  clearMocks:true,// 是否收集测试覆盖率信息
  // collectCoverage: false,​

  // 一个 glob 模式数组,指示应该为其收集覆盖率信息的一组文件
  // collectCoverageFrom: undefined,// 测试覆盖率报错文件输出的目录
  coverageDirectory:"coverage",// 忽略测试覆盖率统计
  // coveragePathIgnorePatterns: [
  //  "/node_modules/"
  // ],​

  // 指示应该使用哪个提供程序检测代码的覆盖率,默认是 babel,可选 v8,但是 v8 不太稳定,建议Node 14 以上版本使用
  // coverageProvider: "babel",​

  // A list of reporter names that Jest uses when writingcoverage reports
  // coverageReporters: [
  //   "json",
  //   "text",
  //   "lcov",
  //   "clover"
  // ],​

  // An object that configures minimum threshold enforcement forcoverage results
  // coverageThreshold: undefined,// A path to a custom dependency extractor
  // dependencyExtractor: undefined,// Make calling deprecated APIs throw helpful error messages
  // errorOnDeprecated: false,// Force coverage collection from ignored files using an arrayof glob patterns
  // forceCoverageMatch: [],​

  // A path to a module which exports an async function that istriggered once before all test suites
  // globalSetup: undefined,​

  // A path to a module which exports an async function that istriggered once after all test suites
  // globalTeardown: undefined,// A set of global variables that need to be available in alltest environments
  // globals: {},// The maximum amount of workers used to run your tests. Canbe specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPUamount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2workers.
  // maxWorkers: "50%",// An array of directory names to be searched recursively upfrom 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 or to arraysof module names that allow to stub out resources with a single module
  // moduleNameMapper: {},​

  // An array of regexp pattern strings, matched against allmodule 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: undefined,​

  // Run tests from one or more projects
  // projects: undefined,​

  // Use this configuration option to add custom reporters toJest
  // reporters: undefined,​

  // Automatically reset mock state between every test
  // resetMocks: false,// Reset the module registry before running each individualtest
  // resetModules: false,// A path to a custom resolver
  // resolver: undefined,// Automatically restore mock state between every test
  // restoreMocks: false,// The root directory that Jest should scan for tests andmodules within
  // rootDir: undefined,​

  // A list of paths to directories that Jest should use tosearch for files in
  // roots: [
  //  "<rootDir>"
  // ],​

  // Allows you to use a custom runner instead of Jest's defaulttest runner
  // runner: "jest-runner",​

  // The paths to modules that run some code to configure or setup the testing environment before each test
  // setupFiles: [],​

  // A list of paths to modules that run some code to configureor set up the testing framework before each test
  // setupFilesAfterEnv: [],// The number of seconds after which a test is considered asslow and reported as such in the results.
  // slowTestThreshold: 5,​

  // A list of paths to snapshot serializer modules Jest shoulduse 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 againstall test paths, matched tests are skipped
  // testPathIgnorePatterns: [
  //  "/node_modules/"
  // ],​

  // The regexp pattern or array of patterns that Jest uses todetect test files
  // testRegex: [],// This option allows the use of a custom results processor
  // testResultsProcessor: undefined,// This option allows use of a custom test runner
  // testRunner: "jasmine2",​

  // This option sets the URL for the jsdom environment. It isreflected in properties such as location.href
  // testURL: "http://localhost",​

  // Setting this value to "fake" allows the use offake timers for functions such as "setTimeout"
  // timers: "real",​

  // A map from regular expressions to paths to transformers
  // transform: undefined,​

  // An array of regexp pattern strings that are matched againstall source file paths, matched files will skip transformation
  // transformIgnorePatterns: [
  //  "/node_modules/",
  //  "\\.pnp\\.[^\\/]+$"
  // ],// An array of regexp pattern strings that are matched againstall modules before the module loader will automatically return a mock for them
  // unmockedModulePathPatterns: undefined,// Indicates whether each individual test should be reportedduring the run
  // verbose: undefined,​

  // An array of regexp patterns that are matched against allsource file paths before re-running tests in watch mode
  // watchPathIgnorePatterns: [],// Whether to use watchman for file crawling
  // watchman: true,
};

Jest CLI Options

参考:jestjs.io/zh-Hans/doc…

指定测试文件运行

  "scripts":{
    "test":"jest ./math.test.js"
  },

Jest 监视模式

watchAll 选项:监视文件的更改并在任何更改时重新运行所有测试。

 "scripts": {
   "test": "jest --watchAll"
  },

Jest API

在测试文件中,Jest 将所有这些方法和对象放入全局环境中。无需要求或导入任何内容即可使用它们。但是,如果喜欢显式导入,则可以:

import { describe, expect, test } from'@jest/globals'

Test 函数

test 函数别名:it(name, fn, timeout)。

test(name,fn, timeout)

test.concurrent(name, fn, timeout)

test.concurrent.each(table)(name, fn, timeout)

test.concurrent.only.each(table)(name, fn)

test.concurrent.skip.each(table)(name, fn)

test.each(table)(name, fn, timeout)

test.only(name, fn, timeout)

只运行当前测试用例

test.only.each(table)(name, fn)

test.skip(name,fn)

test.skip.each(table)(name, fn)

test.todo(name)

创建 global-api.test.js 测试文件,注意,测试文件中必须有一个测试用例,如果没有则直接报错。

test('should ', () => {
  console.log('test--api')
})



test('should ', () => {
  console.log('test--api')
})
test('should1 ', () => {
  console.log('test--api1')
})// 上面两个不运行
test.only('should2 ', () => {
  console.log('test--api2')
})

Expect 匹配器

在编写测试时,通常需要检查值是否满足某些条件。Expect 让我们可以访问许多“匹配器”,以验证不同的内容。

test('two plus two is four', () => {
  expect(2+2).toBe(6)
  expect({ name:'jack' }).toEqual({ name:'jack' })
  expect('Christoph').toMatch(/stop/)
  expect(4).toBeGreaterThan(3)
  expect(4).toBeLessThan(5)
})

完整的匹配器列表查看:jestjs.io/zh-Hans/doc…

describe 函数

describe 创建一个将几个相关测试组合在一起的块。

const myBeverage = {
  delicious:true,
  sour:false,
};describe('my beverage', () => {
  test('is delicious', () => {
    expect(myBeverage.delicious).toBeTruthy();
  });test('is not sour', () => {
    expect(myBeverage.sour).toBeFalsy();
  });
});

分组最直观的就是在测试提示中,有更加友好的信息输出。

describe(name,fn)

describe.each(table)(name, fn, timeout)

describe.only(name,fn)

describe.only.each(table)(name, fn)

describe.skip(name,fn)

describe.skip.each(table)(name, fn)

未完待续。

在这里插入图片描述

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你

关注我的微信公众号【伤心的辣条】免费获取~

送上一句话:

世界的模样取决于你凝视它的目光,自己的价值取决于你的追求和心态,一切美好的愿望,不在等待中拥有,而是在奋斗中争取。

我的学习交流群:902061117 群里有技术大牛一起交流分享~

如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!

好文推荐:

转行面试,跳槽面试,软件测试人员都必须知道的这几种面试技巧!

测试岗反复跳槽,跳着跳着就跳没了…

软件测试人员该学习 Python 的七个理由

App公共测试用例梳理

面试经:一线城市搬砖!又面软件测试岗,5000就知足了…

35岁之后软件测试工程师靠什么养家?我能继续做测试!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值