You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Puppeteer、Cucumber及JS(npm)实现浏览器单例?新手求助Puppeteer浏览器单例配置指导

如何用 Puppeteer + Cucumber + JavaScript 实现浏览器单例模式(新手友好指南)

嘿,作为刚接触JavaScript和Puppeteer的新手,想要在Cucumber测试里实现浏览器单例其实没那么复杂——这样做的核心好处是避免每个测试场景都重新启动浏览器,能大幅提升测试速度,还能减少资源消耗。我来一步步带你搞定整个配置流程!

第一步:初始化项目并安装依赖

首先,咱们先搭好项目基础:

  1. 创建一个新文件夹,打开终端进入它
  2. 初始化npm项目:
    npm init -y
    
  3. 安装所需的开发依赖:
    npm install @cucumber/cucumber puppeteer --save-dev
    
    • @cucumber/cucumber:Cucumber的官方JS版本,用来写BDD测试
    • puppeteer:Chrome官方的无头浏览器自动化工具

第二步:创建浏览器单例管理模块

咱们需要一个专门的文件来管理浏览器实例,确保整个测试生命周期里只有一个浏览器实例在运行。在项目根目录创建browserSingleton.js,代码如下:

let browserInstance = null;

// 获取浏览器单例的方法
async function getBrowser() {
  if (!browserInstance) {
    // 启动浏览器,新手可以把headless设为false,方便看到浏览器操作
    browserInstance = await require('puppeteer').launch({
      headless: 'new', // 最新的无头模式,想可视化就改成false
      args: ['--start-maximized'] // 可选:启动时最大化窗口
    });
    console.log('浏览器已启动');
  }
  return browserInstance;
}

// 关闭浏览器的方法
async function closeBrowser() {
  if (browserInstance) {
    await browserInstance.close();
    browserInstance = null;
    console.log('浏览器已关闭');
  }
}

module.exports = { getBrowser, closeBrowser };

这里的逻辑很简单:用一个变量browserInstance保存浏览器实例,每次调用getBrowser()时,先判断实例是否存在——不存在就启动新浏览器,存在就直接返回已有实例,这样就实现了单例。

第三步:配置Cucumber钩子(Hooks)

Cucumber的钩子能帮我们在测试的不同阶段执行代码,比如所有测试开始前启动浏览器,所有测试结束后关闭它。在项目根目录创建features/support/hooks.js(需要先建features/support文件夹),代码如下:

const { BeforeAll, AfterAll, Before } = require('@cucumber/cucumber');
const { getBrowser, closeBrowser } = require('../../browserSingleton');

let page = null;

// 所有测试场景开始前,只执行一次:启动浏览器
BeforeAll(async function () {
  await getBrowser();
});

// 每个测试场景开始前,创建一个新页面(避免场景之间的状态干扰)
Before(async function () {
  const browser = await getBrowser();
  page = await browser.newPage();
  // 把page实例绑定到Cucumber的World对象里,方便步骤定义里使用
  this.page = page;
});

// 所有测试场景结束后,只执行一次:关闭浏览器
AfterAll(async function () {
  await closeBrowser();
});

这里要注意:每个场景都要创建新页面,而不是复用同一个页面——不然前一个场景的操作会影响后一个场景,比如登录状态、页面内容残留,导致测试结果不准确。

第四步:写一个测试示例验证单例

接下来咱们写一个简单的BDD测试来验证单例是否正常工作:

1. 创建Feature文件

在features文件夹下创建example.feature:

Feature: 浏览器单例测试
  验证Puppeteer浏览器单例在Cucumber测试中正常工作

  Scenario: 访问Google首页
    Given 我打开浏览器
    When 我访问"https://www.google.com"
    Then 页面标题应该包含"Google"

  Scenario: 访问GitHub首页
    Given 我打开浏览器
    When 我访问"https://github.com"
    Then 页面标题应该包含"GitHub"

2. 创建步骤定义文件

在features/step_definitions文件夹下创建steps.js:

const { Given, When, Then } = require('@cucumber/cucumber');
const assert = require('assert');

Given('我打开浏览器', async function () {
  // 这里不需要做额外操作,因为Before钩子已经帮我们创建了page
  console.log('当前使用的是同一个浏览器实例');
});

When('我访问"{string}"', async function (url) {
  await this.page.goto(url);
});

Then('页面标题应该包含"{string}"', async function (expectedText) {
  const pageTitle = await this.page.title();
  assert.ok(pageTitle.includes(expectedText), `页面标题不包含${expectedText},实际标题是:${pageTitle}`);
});

第五步:运行测试

最后,在package.json里添加测试脚本:

{
  "scripts": {
    "test": "cucumber-js"
  }
}

现在在终端运行:

npm test

你会看到终端输出“浏览器已启动”,然后两个场景依次执行,最后输出“浏览器已关闭”——这说明两个场景用的是同一个浏览器实例,单例生效了!

新手注意事项

  • 如果想要看到浏览器的实际操作,把browserSingleton.js里的headless: 'new'改成headless: false即可
  • 单例模式下一定要确保每个场景用新页面,不然会出现测试污染的问题
  • 如果某个特殊场景需要独立的浏览器实例,可以单独写一个钩子来重启浏览器,但大部分测试场景不需要这么做

内容的提问来源于stack exchange,提问作者Saurabh Garg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 13:12:35