最近一直在做业务需求,表单类的,每次调试都要填一堆东西,而且项目需要登录,经常需要来一遍登录流程,再填写表单来调试。这个流程还是比较繁琐的。

于是我在想,自动化测试工具 puppeteer 是可以通过脚本来自动执行浏览器操作的,能不能调试的时候让 puppeteer 帮我自动做了页面操作的一些流程呢?

我试了一下还真可以,用 puppeteer 来自动执行脚本,并且过程中还可以打断点调试,调试体验简直不要太爽。

这篇文章就来分享下。

首先,react 项目我是通过 vscode debugger 来调试的:

把 puppeteer 融入调试流程,调试体验爽翻了!_JavaScript

在 .vscode > launch.json 的调试配置文件里新增一个 chrome 类型的调试配置,输入调试的 url。

然后点击 debug 启动:

把 puppeteer 融入调试流程,调试体验爽翻了!_断点调试_02

执行到代码中的断点就会在 vscode 里断住:

把 puppeteer 融入调试流程,调试体验爽翻了!_JavaScript_03

这样就可以在 vscode 里断点调试 react 项目了。

但登录还是比较麻烦的,用户名密码我经常忘,而且登录之后还要填一些表单,也很麻烦。

这时候我想到了 puppeteer。

它是可以自动执行脚本的。

比如这样一段脚本:

const puppeteer = require('puppeteer');

(async () => {
const browser = await puppeteer.launch({
headless: false
});

const page = await browser.newPage();
await page.goto('http://localhost:8000/login');

await page.waitForSelector('#username');

const $username = await page.$('#username');
await $username.type('1111111', {
delay: 100
});

const $password = await page.$('#password');
await $password.type('testtest', {
delay: 100
});

const $button = await page.$('button[type="submit"]');
await $button.click();
})();

用 puppeteer 启动一个浏览器,headless 设为 false 就是需要界面。

打开新页面,加载 login 的 url,等出现 #username 的元素之后之后,输入用户名和密码,然后点击提交。

这个脚本还是很容易理解的。

跑下试试:

把 puppeteer 融入调试流程,调试体验爽翻了!_断点调试_04

流程倒是对了,只是显示的不对,加个 viewport 的设置就好了:

把 puppeteer 融入调试流程,调试体验爽翻了!_自动化测试_05

width、height 为 0 会自适应。

把 puppeteer 融入调试流程,调试体验爽翻了!_断点调试_06

自动跑登录脚本成功了。

那问题来了,断点调试和自动化测试能不能一起跑呢?

看起来这俩都是跑了一个浏览器,应该能融合才对。

这个就要从 puppeteer 和调试的实现原理来看了。

调试是基于调试协议的,比如网页调试是 Chrome DevTools Protocol。

Chrome DevTools 对接了 CDP 可以调试网页,我们用 VSCode Debugger 能调试网页同样也是对接了 CDP。

把 puppeteer 融入调试流程,调试体验爽翻了!_前端_07

puppeteer 能控制浏览器执行一些脚本,也是基于 CDP。

这俩都需要浏览器在调试模式启动,也就是指定 remote-debugging-port。

我们前面跑 react 项目的调试是用的 launch 的方式,它会自动跑一个调试浏览器,然后连接上 ws 调试服务。

其实它还有 attach 的方式:

把 puppeteer 融入调试流程,调试体验爽翻了!_自动化测试_08

attach 的方式不单独跑调试浏览器,而是连接上已有的浏览器来调试。所以需要指定调试服务的端口。

既然 puppeteer 和调试都要以调试模式跑浏览器,那我们就等 puppeteer 跑起 chrome 之后,vscode debugger 再 attach 上它来调试。

这样不就既能自动化测试,又能断点调试了么?

我们来试一下:

把 puppeteer 融入调试流程,调试体验爽翻了!_前端_09

puppeteer 启动 chrome 的时候,我指定了调试端口为 9999。

然后跑下 puppeteer 脚本,把 chrome 跑起来:

把 puppeteer 融入调试流程,调试体验爽翻了!_前端_10

之后,去 react 项目里启动调试,只不过这次是 attach:

把 puppeteer 融入调试流程,调试体验爽翻了!_断点调试_11

依然是能正常断点调试的:

把 puppeteer 融入调试流程,调试体验爽翻了!_前端_12

而且现在还可以跑自动化脚本了!

我们改造下 puppeteer 脚本,改成每次输入内容的时候才跑对应的脚本。

const puppeteer = require('puppeteer');
const readline = require('readline');

(async () => {
const browser = await puppeteer.launch({
headless: false,
defaultViewport: {
width: 0,
height: 0
},
debuggingPort: 9999
});

const page = await browser.newPage();

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});

rl.on('line', async (str) => {
if (str === 'login') {
await login(page);
} else if (str === 'baidu') {
await baidu(page);
}
});
})();

async function baidu(page) {
await page.goto('http://www.baidu.com');
}
async function login(page) {
await page.goto('http://localhost:8000/login');

await page.waitForSelector('#username');

const $username = await page.$('#username');
await $username.type('1111111', {
delay: 100
});

const $password = await page.$('#password');
await $password.type('testtest', {
delay: 100
});

const $button = await page.$('button[type="submit"]');
await $button.click();
}

主要是加了 readline 模块,这是 node 内置模块,用于一行行读取流的输入用的。

把 puppeteer 融入调试流程,调试体验爽翻了!_Node.js_13

我指定了输入 baidu 的时候打开 baidu,输入 login 的时候打开 localhost:8000,然后执行登录脚本。

试一下:

把 puppeteer 融入调试流程,调试体验爽翻了!_断点调试_14

然后我们把 vscode debugger 也 attach 上。

之后再跑 puppeteer 的脚本。

猜下这时候会发生什么?

把 puppeteer 融入调试流程,调试体验爽翻了!_JavaScript_15

执行了自动化测试脚本,并且还在断点处断住了!

这样我们就不用再手动点点点,可以用脚本自动跑一些流程,而且还可以断点调试这个流程。

我们再改一下脚本:

把 puppeteer 融入调试流程,调试体验爽翻了!_自动化测试_16

click 之后,又输入了密码,然后再 click:

把 puppeteer 融入调试流程,调试体验爽翻了!_Node.js_17

断住的时候浏览器不会执行代码,这时候自动化脚本也就执行不了,可以专心根据调用栈作用域等调试代码,调试完之后,释放断点,自动化脚本才会继续执行。

这样我们就完美的把 puppeteer 的自动化测试和 VSCode Debugger 的网页断点调试结合在了一起。

总结

我们会用 VSCode Debugger 断点调试网页,会用 puppeteer 写自动化测试的脚本来测试某条流程。

这俩其实完全可以结合在一起用,因为他们都是基于 CDP,会启动一个调试模式的浏览器。只要 VSCode Debugger attach 到 puppeteer 启动的浏览器就好了。

融合在一起之后,你可以写 puppeteer 脚本来自动化一些流程,比如自动登录、自动填写表单等,这个过程还可以断点调试,断点释放之后再执行后续自动化脚本。

两者简直是天作之合。

把 puppeteer 融入调试流程,调试体验爽翻了!