跳转到内容

前端自动化测试

刀刀
0字
0分钟
2026/8/16

无头浏览器

首先认识一下无头浏览器,无头浏览器是一个没有 UI 界面的模拟浏览器,可以用NodeJS配合对应的库模拟用户在浏览器浏览的操作。

常见的无头浏览器有:Puppeteer【推荐】PhantomJS【老牌,不维护】Selenium等。

思路实现

  1. 编写NodeJS脚本代码
  2. 脚本 link 为全局指令(可不做)
  3. js 代码立体无头浏览器模拟访问
  4. 获取各项指标

首先搭建一个简单的项目,目录结构如下:

txt
├── node_modules
├── index.js
├── package.json

下载 puppeteer-core 依赖,这里不下载 puppeteer 是因为该模块比较大,它包含了浏览器内核,而 puppeteer-core 是一个轻量级的模块,只包含浏览器控制功能。

index.js 文件内引入 puppeteer-core,并编写代码:

js
const puppeteer = require('puppeteer-core')

;(async () => {
  // 打开浏览器
  const browser = await puppeteer.launch({
    headless: false, // 是否无头,设置为 `false` 可以看到浏览器界面,设置为 `true` 则会在后台运行,看不到浏览器界面
    executablePath:
      'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', // 浏览器路径
    defaultViewport: null, // 默认视窗大小 { width: 1920, height: 1080 }
  })

  // 打开新页签
  const page = await browser.newPage()
  // 跳转前到开始时间
  const startTime = performance.now()
  // 页签跳转到一个新地址(案例中是跳转到百度)
  await page.goto('https://www.baidu.com')
  console.log(Date.now() - startTime) // 跳转耗时
  browser.close() // 关闭浏览器
})()

终端命令行运行 node index.js,即可成功打开浏览器。

想要获取项目各项指标,无需手动记录时间去计算,performance 对象可以帮我们记录各项指标,不过此时是在Node环境,Node环境没有这个事件对象,因此需要搭配 page.evaluate 方法,将 performance 对象传递到浏览器环境,在浏览器环境获取各项指标。

js
const puppeteer = require('puppeteer-core')

;(async () => {
  // 打开浏览器
  const browser = await puppeteer.launch({
    headless: false, // 是否无头,设置为 `false` 可以看到浏览器界面,设置为 `true` 则会在后台运行,看不到浏览器界面
    executablePath:
      'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', // 浏览器路径
    defaultViewport: null, // 默认视窗大小 { width: 1920, height: 1080 }
  })

  // 打开新页签
  const page = await browser.newPage()
  // 跳转前到开始时间
  const startTime = performance.now()
  // 页签跳转到一个新地址(案例中是跳转到百度)
  await page.goto('https://www.baidu.com')
  await page.evaluate(() => {
    return JSON.stringify(window.performance.timing) 
  }) 
  console.log(Date.now() - startTime) // 跳转耗时
  browser.close() // 关闭浏览器
})()

明确

  1. 如果想要获取打开的页面里面的东西,如 window 对象,需要在 page.evaluate 方法内获取。
  2. 如果想把页面的对象传到Node,则需要通过 JSON.stringify 返回回来。

可做的事

  1. 最常见的页面首屏渲染,DOM渲染时间等
  2. 页面某个核心区域出现的时间,因为区域的内容需要异步请求从后端来才能渲染,所以需要记录这个区域渲染完成的时间
  3. 某个操作链路完成的时间(不考虑人类反应速度,单纯程序本身完成这个操作链路需要的时间)

有一个项目通过 v-for="item in list" 来渲染表格数据,想要知道该组件渲染完成的时间,可以借助 page.waitForSelector 方法,等待某个元素出现,然后记录时间。

vue
<template>
  <div class="app">
    <div class="list-item" v-for="item in list" :key="item.id">
      {{ item.name }}
    </div>
  </div>
</template>
js
const puppeteer = require('puppeteer-core')

;(async () => {
  // 打开浏览器
  const browser = await puppeteer.launch({
    headless: false, // 是否无头,设置为 `false` 可以看到浏览器界面,设置为 `true` 则会在后台运行,看不到浏览器界面
    executablePath:
      'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', // 浏览器路径
    defaultViewport: null, // 默认视窗大小 { width: 1920, height: 1080 }
  })

  // 打开新页签
  const page = await browser.newPage()
  // 跳转前到开始时间
  const startTime = performance.now()
  // 页签跳转到一个新地址(案例中是跳转到百度)
  await page.goto('https://www.baidu.com') 
  await page.goto('http://locahost:5173') 
  await page.waitForSelector('.list-item') // 获取对应的组件
  console.log(Date.now() - startTime) // 渲染耗时
  browser.close() // 关闭浏览器
})()

另一个项目想要测试点击登录按钮后,登录成功后跳转到首页的时间【不想计算上人类输入点击等的时间进去】,可以借助 page.waitForNavigation 方法,等待页面跳转完成,然后记录时间。

vue
<template>
  <div class="app">
    <button v-if="xx" class="login" @click="login">登录</button>

    <template v-else>
      <input class="username" type="text" placeholder="用户名" />
      <input class="password" type="password" placeholder="密码" />
      <button class="confirm-btn" @click="confirm">提交</button>
    </template>
  </div>
</template>
js
const puppeteer = require('puppeteer-core')

;async () => {
  // 打开浏览器
  const browser = await puppeteer.launch({
    headless: false, // 是否无头,设置为 `false` 可以看到浏览器界面,设置为 `true` 则会在后台运行,看不到浏览器界面
    executablePath:
      'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', // 浏览器路径
    defaultViewport: null, // 默认视窗大小 { width: 1920, height: 1080 }
  })

  // 打开新页签
  const page = await browser.newPage()
  // 跳转前到开始时间
  const startTime = performance.now()
  // 页签跳转到一个新地址(案例中是跳转到百度)
  await page.goto('https://www.baidu.com') 
  await page.goto('http://locahost:5173/login') 
  const loginBtn = await page.$('.login') // 获取登录按钮
  await loginBtn.click() // 模拟点击
  const user = await page.waitForSelector('.username') // 获取对应的用户输入框
  await user.type('admin') // 输入用户名
  const pass = await page.$('.password') // 获取对应的密码输入框,能够获取到用户输入框说明密码输入框也出来了,可以直接 page.$
  await pass.type('123456') // 输入密码
  const comfirmBtn = await page.$('.comfirm-btn') // 获取对应的提交按钮
  await comfirmBtn.click() // 模拟点击
  await page.waitForNavigation() // 等待页面跳转完成
  console.log(Date.now() - startTime) // 操作耗时
  browser.close() // 关闭浏览器
}

拓展

page.waitForSelector 方法和 page.$ 方法都是用于获取页面的 DOM 元素,二者区别在于 page.waitForSelector 方法会等待元素出现在页面上,而 page.$ 方法则不会等待,只要元素存在即可。因此如果确保元素已经出现在页面上,可以使用 page.$ 方法,否则可以使用 page.waitForSelector 方法。

全局工具

package.json 中配置 bin 对象,添加命令,配置对应要执行的 js 文件。在 index.js 文件中首行添加注释,表示该文件内容需要在 Node 环境下运行。

json
{
  "bin": {
    "mytest": "./index.js"
  }
}
js
#! /usr/bin/env node

终端输入命令 npm link 即可发布到本地全局。如果想要不写死地址,而是在命令行后面加上地址测试,可以修改一下 index.js 文件获取地址那段代码,通过 process.argv 获取命令行携带的地址。

js
await page.goto('https://www.baidu.com') 
await page.goto(process.argv[2] || 'https://www.baidu.com') 
sh
mytest https://www.bilibili.com

类推扩展

工程化建设一大方向为项目自动化脚本建设,通过编写脚本,实现项目构建、测试、部署等自动化,提升开发效率,降低人工成本。

  1. 项目经常需要基于模板 copy ,然后做一些更改,可以编写一个自动化 copy 脚本,copy 的时候自动把 copy 后要进行的一些更改改好,不需要 copy 后自己再去改
  2. 页面模板创建脚本
  3. 根据接口文档自动生成 mock 文件

贡献者

The avatar of contributor named as duyidao duyidao
The avatar of contributor named as v_duyilin v_duyilin
The avatar of contributor named as 刀刀 刀刀

页面历史

刀刀博客累计访客 人;文档累计访问量共