Node模拟真实浏览器环境对请求进行测试

1,075 阅读2分钟

核心:使用Google Puppeteer

  • Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过DevTools协议控制 Chromium 或 Chrome。Puppeteer 默认以headless模式运行,但是可以通过修改配置文件运行“有头”模式

说白了就是Puppeteer可以让代码在一个Chrome浏览器中执行,但是浏览器不需要人为打开

通过Puppeteer可以实现诸多浏览器操作,例如:

  • 生成页面 PDF
  • 抓取 SPA(单页应用)并生成预渲染内容(即“SSR”(服务器端渲染))
  • 自动提交表单,进行 UI 测试,键盘输入等
  • 创建一个时时更新的自动化测试环境。 使用最新的 JavaScript 和浏览器功能直接在最新版本的Chrome中执行测试
  • 捕获网站的timeline trace,用来帮助分析性能问题
  • 测试浏览器扩展
  • 对页面进行脚本操作

但是,由于安全策略问题,Puppeteer不支持通过page.evaluate()方法通过node向浏览器注入脚本进行网络请求:包括post、get、websocket连接等等

所以如果要通过Node模拟真实浏览器环境对请求进行测试,可是自定义一个html文件,并在其中提前预置相应的测试脚本,然后在node环境下通过page.evaluate方法对预置脚本进行传参和调用,获取相应的信息

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Connect Test Enviroment</title>
    <script>
      // 测试WebSocket连接
      const testWebSocket = async (url) => {
        const ws = new WebSocket(url);
        const connectedPromise = new Promise((resolve) => {
          ws.addEventListener('open', () => {
            resolve(true);
          });
        });
        const errorPromise = new Promise((resolve) => {
          ws.addEventListener('error', (error) => {
            resolve(false);
          });
        });
        const result = await Promise.race([connectedPromise, errorPromise]);
        ws.close();
        return result;
      };
​
      // 测试Post请求
      const testPost = async (url) => {
        try {
          const response = await fetch(url, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
          });
          const responseData = await response.json();
          return responseData;
        } catch (error) {
          return error;
        }
      };
​
      // 测试Get请求
      const testGet = async (url) => {
        const requestOptions = {
          method: 'GET',
          redirect: 'follow',
        };
        try {
          const response = await fetch(url);
          return response.status;
        } catch (error) {
          return error;
        }
      };
​
      // 挂载至window对象
      window.testWebSocket = testWebSocket;
      window.testPost = testPost;
      window.testGet = testGet;
    </script>
  </head>
  <body></body>
</html>

在node环境中使用如下代码对脚本进行调用

const puppeteer = require('puppeteer');
​
// 测试websocket连接
const testWebScoket = async (url) => {
  const browser = await puppeteer.launch({headless: 'new'});
  const page = await browser.newPage();
  await page.goto(/** html文件绝对路径,并以file://开头 **/);
  const result = await page.evaluate(async (url) => {
    const data = await testWebSocket(url);
    return data;
  }, url);
  await browser.close();
  return result;
};
​
// 测试post请求
const testPost = async (url) => {
  const browser = await puppeteer.launch({headless: 'new'});
  const page = await browser.newPage();
  await page.goto(/** html文件绝对路径,并以file://开头 **/);
  const result = await page.evaluate(async (url) => {
    const data = await window.testPost(url);
    return data;
  }, url);
  await browser.close();
  return result;
};
​
// 测试get请求
const testGet = async (url) => {
  const browser = await puppeteer.launch({headless: 'new'});
  const page = await browser.newPage();
  await page.goto(/** html文件绝对路径,并以file://开头 **/);
  const result = await page.evaluate(async (url) => {
    const data = await window.testGet(url);
    return data;
  }, url);
  await browser.close();
  return result;
};
​
(async () => {
  console.log(await testWebScoket(/** 需要测试的websocket:wss://xxx:xxx **/));
  console.log(await testPost(/** 需要测试的url字符串:https://xxx:8810/xxx/xxx/xxx **/));
  console.log(await testGet(/** 需要测试的url字符串:https://xxx:8810/xxx/xxx/xxx **/));
})();

即可实现模拟真实浏览器环境

  • 踩坑:报错Puppeteer Evaluation failed: ReferenceError: __awaiter is not defined

通常情况下出现这个问题是因为采用了typescript,然后tsc将代码编译成了版本较低的js,此时需要对tsconfig.json进行修改

{
  "compilerOptions": {
    "target": "ES2017",
    ...
  }
}