puppeteer 自动性能测试

2019-05-24  本文已影响0人  yrzhaoweb

目的

用puppeteer对多个移动端页面进行性能测试,要求同一页面多次运行求平均值,结果导出为excel文件,需要wifi环境和弱网环境两份测试数据。

开始前

先简单的了解一下puppeteer 是什么? 先附上官方中文文档
官方的回答是:

Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过 DevTools协议控制 Chromium 或 Chrome。Puppeteer 默认以 headless模式运行,但是可以通过修改配置文件运行“有头”模式。你可以在浏览器中手动执行的绝大多数操作都可以使用 Puppeteer 来完成!

安装步骤:

cnpm i puppeteer

用cnpm安装较快,因为安装puppeteer的同时会下载一个大小170MB~250MB的chromium浏览器(按操作系统而定)。

先来一个最简单的例子:

const puppeteer = require('puppeteer')

const test = (async () => {
  // 创建一个浏览器实例
  // 此处传入配置项设置headless为false,否则程序将在后台执行,不会打开浏览器
  const browser = await puppeteer.launch({headless: false}) 
  // 在刚才的浏览器上打开一个空页面
  const page = await browser.newPage()
  // 等待页面创建完成后跳转到简书官网
  await page.goto('https://www.jianshu.com')
})

test()

运行这段代码node将会开启一个chromium浏览器打开一个页面跳转到首页。

开始

现在我们已经可以打开一个页面,那么接下来

1 如何获取页面性能数据

在平时调试中我们通常用两种方法来看页面性能数据:
window.performance.timing 和 chrome的DevTools中的Performance工具。
经查阅得知,window.performance.timing是执行于网页上下文的,而DevTools是执行于浏览器上下文的。这边给出获取两种数据的代码

const puppeteer = require('puppeteer')

const test = (async () => {
  const browser = await puppeteer.launch({headless: false})
  const page = await browser.newPage()
  await page.goto('https://www.jianshu.com')
  // 获取 DevTools 中的数据
  let devData = await page._client.send('Performance.getMetrics')
  // 获取 window 中的数据
  let windowData = JSON.parse(await page.evaluate(() => {
    JSON.stringify(window.performance.timing
  })
})

test()

本次需求只需要得到页面的白屏时间和整屏时间,选择window中的数据是最便捷的,若想获取其他更为高级的数据,可选用DevTools中的数据。

2 对单个页面测试

为了最大化减少网速对页面加载的影响,我们单次只开启一个浏览器,一个页面,收集到这次数据后关闭浏览器。在chromium中关闭浏览器即可清空缓存,下次打开浏览器时所有数据都会重新加载,故检测一个页面的性能可使用如下代码

const puppeteer = require('puppeteer')

const url = 'https://www.jianshu.com'
const times = 20
const time = 3000
let result = []
let temp = []

const test = (async () => {
  for (let i = 0; i < times; i++) {
    let browser = await puppeteer.launch({headless: false})
    let page = await browser.newPage()
    await page.goto(url)
    await page.waitFor(time)
    let timeData = JSON.parse(await page.evaluate(
      () => JSON.stringify(window.performance.timing)
    ))
    temp.push(dataHandler(timeData))
    await browser.close()
  }

  result.push(url)
  let domSum = 0
  let holeSum = 0
  for (let k = 0; k < times; k++) {
    domSum += temp[k].domTime
    holeSum += temp[k].holeTime
  }
  result.push(domSum / times)
  result.push(holeSum / times)
  console.log(result)
})

const dataHandler = (timeData) => {
  let result = {}
  result.holeTime = timeData.loadEventEnd - timeData.domainLookupStart //整屏时间
  result.domTime = timeData.domContentLoadedEventStart  - timeData.domainLookupStart //白屏时间
  return result
}

test()

以上代码即可对简书官网进行20次性能测试求平均值。输出结果如下


牛刀小试
3 如何将数据转为excel文件

对了了各种将json转为excel的包,最终选择了功能和上手难度都挺均衡的node-xlsx

代码如下

const fs = require('fs')
const xlsx = require('node-xlsx')

const output = (async (timeData) => {
  let data = [['所属', '链接', '平均白屏时间/ms', '平均整屏时间/ms']]
  let len = timeData.length
  for (let i = 0; i < len; i++) {
    data.push(timeData[i])
  }
  // 设置单元格列宽
  const xlsxOption = {'!cols': [{ wch: 15 }, { wch: 100 }, { wch: 15 }, { wch:15 }]}
  var buffer = xlsx.build([{name: "mySheetName", data: data}], xlsxOption); 
  let time = new Date().getTime()
  fs.writeFileSync(`./${time}.xlsx`, buffer, 'binary')
})

重构

以上我们已经可以对一个页面进行性能测试了,但是要完成需求还需要进一步完善代码。完善,先从重构开始
以下是重构后的目录结构及代码

目录结构

index.js

const performanceTest = require('./src/performanceTest')

performanceTest()

urlList.js

module.exports = [
  {name: '简书', url: 'https://www.jianshu.com'},
  {name: '百度', url: 'https://www.baidu.com'},
  {name: '淘宝', url: 'https://www.taobao.com'}
]

performanceTest.js

const urlTest = require('./urlTest')
const urlList = require('./urlList')
const output = require('./output')

let times = 20  // 单个页面循环次数
let time = 2000 // 
const performanceTest = (async () => {
  const len = urlList.length
  let result = []
  const options = {
    headless: false // 默认为true,不会出现浏览器,后台静默执行脚本,设置为false后会自动弹浏览器执行脚本
  }
  for (let i = 0; i < len; i++) {
    result.push(await urlTest(options, urlList[i], times, time))
    // 获取urlTest方法返回的数据并存入result中
  }
  output(result) // 导出为xlsx文件
})

module.exports = performanceTest

urlTest.js

const puppeteer = require('puppeteer')
const dataHandler = require('./dataHandler')

const urlTest = (async (options, obj, times = 1, time = 1000) => {
  let result = []
  let temp = []
  for (let j = 0; j < times; j++) {
    let browser = await puppeteer.launch(options).catch((e) => {
      let t = new Data().getTime()
      console.log(t, obj, j, e)
      urlTest(options, obj, times, time)
    })
    let page = await browser.newPage()
    try {
      await page.goto(obj.url)
    } catch (e) {
      let t = new Data().getTime()
      console.log(t, obj, j, e)
      urlTest(options, obj, times, time)
    }
    await page.waitFor(time)
    let timeData = JSON.parse(await page.evaluate(
      () => JSON.stringify(window.performance.timing)
    ))
    temp.push(dataHandler(timeData))
    browser.close()
  }
  result.push(obj.name)
  result.push(obj.url)
  let domSum = 0
  let holeSum = 0
  for (let k = 0; k < times; k++) {
    domSum += temp[k].domTime
    holeSum += temp[k].holeTime
  }
  result.push(domSum / times)
  result.push(holeSum / times)
  return result
})

module.exports = urlTest

dataHandler.js

const dataHandle = (timeData) => {
  let result = {}
  result.holeTime = timeData.loadEventEnd - timeData.domainLookupStart //整屏时间
  result.domTime = timeData.domContentLoadedEventStart  - timeData.domainLookupStart //白屏时间
  return result
}

module.exports = dataHandle

output.js

const fs = require('fs')
const xlsx = require('node-xlsx')

const output = (async (timeData) => {
  let data = [['所属', '链接', '平均白屏时间/ms', '平均整屏时间/ms']]
  let len = timeData.length
  for (let i = 0; i < len; i++) {
    data.push(timeData[i])
  }
  const xlsxOption = {'!cols': [{ wch: 15 }, { wch: 100 }, { wch: 15 }, { wch:15 }]}
  var buffer = xlsx.build([{name: "mySheetName", data: data}], xlsxOption); // Returns a buffer
  let time = new Date().getTime()
  fs.writeFileSync(`./src/result/${time}.xlsx`, buffer, 'binary')
})

module.exports = output

完善

1将页面以移动端的方式打开

puppeteer有自带的包,可以将页面设置为移动端模式再进行操作

const devices = require('puppeteer/DeviceDescriptors')
const iPhone = devices['iPhone 6']

将变量iPhone作为参数传入page.emulate()方法中即可以移动端模式打开页面。

2 对网速进行限制

这边我们要用DevTools中的Network对网速进行限制,代码如下

await page._client.send('Network.emulateNetworkConditions', {
      offline: false,
      latency: 200, // ms
      downloadThroughput: 780 * 1024 / 8, // 780 kb/s
      uploadThroughput: 330 * 1024 / 8, // 330 kb/s
    });

结语

puppeteer的功能十分强大,本次测试只是用到了九牛一毛而已(坐我旁边的大佬用puppeteer写了个域名防封杀系统),但是不管怎么说我已经可以通过这些简单的脚本获取到我想要的数据。

最后附上一个 DevTools Protocol 链接,可以通过page._client.send()方法向chromium发送DevTools原始指令

小技能+1。

路漫漫其修远兮,吾将上下而求索。

上一篇下一篇

猜你喜欢

热点阅读