对于E2E测试的一些思考

为什么要做e2e

前端逻辑越来越复杂,仅单测或接口测试已经无法模拟出用户端的实际操作,无法覆盖到前后端逻辑结合后的业务场景:

// 公用页面 common.html
axios.post('/api', {
  serviceCode: this.$route.query.serviceCode || this.confMap.serviceCode
})

// 第一次,小明接需求,接了图文问诊,实现方式:通过生态后台配置了 confMap.serviceCode,值为 tuwen
// 第二次,小红接需求,接了电话问诊,实现方式:从入口页面携带query common.html?serviceCode=dianhua

单看代码很难发现问题,且对于后端来说也不是错误的参数。但在业务上来说,的的确确会阻塞流程

E2E(End to End)测试

又叫端对端测试,是一种从头到尾测试整个软件产品,以确保应用程序流按预期运行的技术。它定义了产品的系统依赖关系,并确保所有集成部件按预期协同工作。

软件测试

  • 以测试过程中执行状态分类:静态测试和动态测试
  • 以具体实现算法细节和系统内部结构的相关情况可分为:黑盒测试、白盒测试和灰盒测试
  • 从程序执行的方式来分类,可分为人工测试和自动化测试

测试框架横向对比

image.png

原文

testcafe小试牛刀

  1. 安装 npm i testcafe
  2. 编写测试用例代码 demo.js:
  3. 执行用例:npx testcafe chrome demo.js
  // demo.js
  /*
    1. 打开一个在线的信息编码工具网站
    2. 验证工具解码是否正确: 即输出 %E5%BE%AE%E5%8C%BB 解码后,是否是'微医'
   */
  import { Selector, t } from 'testcafe'

  fixture `我是demo`
    .page `https://www.baidufe.com/fehelper/index/index.html`

  test('我是demo', async (t) => {
    await t
      .click(Selector('.section-container .title').withText('信息编码转换').parent().find('a'))
      .click(Selector('.x-opts .radio').withText('URL解码').find('input'))
      .typeText('#srcText', '%E5%BE%AE%E5%8C%BB')
      .click('#btnCodeChange')
      t.expect(Selector('#rstCode').value).eql('微医', '转换出错')
  })

配置测试进程运行时的环境信息

js实例配置

  1. 创建 index.js:
  2. 运行:node index.js
// index.js
const createTestCafe = require('testcafe');
(async function () {
  const testcafe = await createTestCafe('localhost', 1337, 1338)
  try {
    const runner = testcafe.createRunner()

    const failedCount = await runner
      .src(['./core/testcase/demo.js'])
      // .browsers(['edge:emulation:device=iphone X'])
      .browsers(['edge:emulation:width=1366;height=768'])
      // 错误自动截图
      .screenshots({
          path: './screenshorts/',
          takeOnFails: true,
          pathPattern: '${DATE}_${TIME}/test-${TEST_INDEX}/${USERAGENT}/${FILE_INDEX}.png'
      })
      // .reporter(['spec', {
      //     name: 'json',
      //     output: 'reports/report.json'
      // }])
      .reporter('cucumber-json')
      .run({
        skipJsErrors: true, // 页面js错误是否忽略
        quarantineMode: true, // 隔离模式,可以理解为失败重跑
        selectorTimeout: 15000, // 设置页面元素查找超时时间,智能等待
        assertionTimeout: 7000, // 设置断言超时时间
        pageLoadTimeout: 30000, // 设置页面加载超时时间
        debugOnFail: !true, // 失败开启调试模式 脚本编写建议开启
        speed: 1 // 执行速度0.01 - 1
      })

    console.log('Tests failed: ' + failedCount)
  }
  finally {
    await testcafe.close()
  }
})()

其他方式

  • 根目录创建配置文件 .testcaferc.json (优先级高于js(runner实例)文件)
  • 指令方式配置 npx testcafe remote demo.js --qr-code --selector-timeout 50000

如何进行异常分析

错误流程截图

通过配置screenshots,可以对测试的流程进行截图(支持headless)

  runner
    .screenshots({
        path: 'record/screenshorts',
        takeOnFails: true,
        pathPattern: '${DATE}_${TIME}/test-${TEST_INDEX}/${USERAGENT}/${FILE_INDEX}.png'
    })

测试流程录屏

  • 依赖本地FFmpeg插件(testcafe提供)
  runner
    .video('record/videos')

过分依赖环境

  • 通过注入rrweb.js,将rrweb生成的数据通过接口发送到后端。最终通过还原数据实现录像的功能
  runner
    .clientScripts('scripts/rrweb-record.min.js', 'scripts/index.js')

可以通过testcafe提供的外挂js的方式实现录屏,且rrweb可以将页面的接口请求记录下来(还可以查看dom信息)

测试报告分析

默认是标准的测试报告 spec,不太好看。

testcafe支持安装第三方插件进行报告生成 npm i testcafe-reporter-cucumber-json multiple-cucumber-html-reporter

  1. 配置测试报告插件:
runner
  .reporter('cucumber-json')
  1. 将测试报告生成页面: report-generator.js:
const report = require('multiple-cucumber-html-reporter');
const path = require('path');
const projectName = path.basename(__dirname);
const projectVersion = process.env.npm_package_version;
const reportGenerationTime = new Date().toISOString();
report.generate({
  reportName: 'TestCafé Report',
  jsonDir: 'cucumber-json-reports',
  reportPath: 'cucumber-json-reports/html',
  openReportInBrowser: !true,
  disableLog: true,
  displayDuration: true,
  displayReportTime: true,
  durationInMS: true,
  customData: {
    title: 'Run info',
    data: [
      { label: 'Project', value: `${projectName}` },
      { label: 'Release', value: `${projectVersion}` },
      { label: 'Report Generation Time', value: `${reportGenerationTime}` },
    ],
  },
});

还能做什么

  • 测试实例浏览器窗口注入js实现错误上报、录屏等功能(如rrweb.js实现dom录屏)
  • 接口拦截器(监听页面运行中接口报错)
  • 电商自动下单、使用”测试“代替人工重复性的点点点操作(验证码?)

缺点

  • 编写成本,需要亿点点的前端基础
  • 维护成本,页面上一个文本的改动可能就涉及测试用例代码的重新发版

个人思路

  1. 使用json编写。解析器,能够将JSON解析为testcafe识别的代码,并将其执行 image.png
  2. 通过web后台控制测试。后台页面与本地后端服务进行通信,调用testcafe进程运行测试任务 image.png
  3. 自动生成json。浏览器插件,点击页面,监听dom元素,生成json数据

testcafe提供的方案

testcafestudio

演示视频