跳转到内容

前端自动化测试

刀刀

4/8/2025

0 字

0 分钟

    学习明灯
  • UP主:三十的前端课,视频地址:前往学习

无头浏览器

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

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

思路实现

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

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

├── 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 文件