前端自动化测试
无头浏览器
首先认识一下无头浏览器,无头浏览器是一个没有 UI 界面的模拟浏览器,可以用NodeJS配合对应的库模拟用户在浏览器浏览的操作。
常见的无头浏览器有:Puppeteer【推荐】、PhantomJS【老牌,不维护】、Selenium等。
思路实现
- 编写NodeJS脚本代码
- 脚本
link为全局指令(可不做) js代码立体无头浏览器模拟访问- 获取各项指标
首先搭建一个简单的项目,目录结构如下:
├── node_modules
├── index.js
├── package.json下载 puppeteer-core 依赖,这里不下载 puppeteer 是因为该模块比较大,它包含了浏览器内核,而 puppeteer-core 是一个轻量级的模块,只包含浏览器控制功能。
在 index.js 文件内引入 puppeteer-core,并编写代码:
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 对象传递到浏览器环境,在浏览器环境获取各项指标。
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() // 关闭浏览器
})()明确
- 如果想要获取打开的页面里面的东西,如
window对象,需要在page.evaluate方法内获取。 - 如果想把页面的对象传到Node,则需要通过
JSON.stringify返回回来。
可做的事
- 最常见的页面首屏渲染,DOM渲染时间等
- 页面某个核心区域出现的时间,因为区域的内容需要异步请求从后端来才能渲染,所以需要记录这个区域渲染完成的时间
- 某个操作链路完成的时间(不考虑人类反应速度,单纯程序本身完成这个操作链路需要的时间)
有一个项目通过 v-for="item in list" 来渲染表格数据,想要知道该组件渲染完成的时间,可以借助 page.waitForSelector 方法,等待某个元素出现,然后记录时间。
<template>
<div class="app">
<div class="list-item" v-for="item in list" :key="item.id">
{{ item.name }}
</div>
</div>
</template>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 方法,等待页面跳转完成,然后记录时间。
<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>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 环境下运行。
{
"bin": {
"mytest": "./index.js"
}
}#! /usr/bin/env node终端输入命令 npm link 即可发布到本地全局。如果想要不写死地址,而是在命令行后面加上地址测试,可以修改一下 index.js 文件获取地址那段代码,通过 process.argv 获取命令行携带的地址。
await page.goto('https://www.baidu.com')
await page.goto(process.argv[2] || 'https://www.baidu.com') mytest https://www.bilibili.com类推扩展
工程化建设一大方向为项目自动化脚本建设,通过编写脚本,实现项目构建、测试、部署等自动化,提升开发效率,降低人工成本。
- 项目经常需要基于模板
copy,然后做一些更改,可以编写一个自动化copy脚本,copy的时候自动把copy后要进行的一些更改改好,不需要copy后自己再去改 - 页面模板创建脚本
- 根据接口文档自动生成
mock文件