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

Circle CI中Enzyme浅渲染测试引发Puppeteer Promise收集协议错误

解决Circle CI中Meteor + Puppeteer + Enzyme偶发的Promise收集异常问题

问题背景

在Circle CI环境里运行Meteor测试(基于meteortesting:mocha和Puppeteer驱动)时,偶发会在所有测试执行完成后出现未处理的Promise拒绝异常,还伴随CI超时错误。异常日志如下:

W20200612-14:11:02.429(0)? (STDERR) (node:642) UnhandledPromiseRejectionWarning: Error: Protocol error (Runtime.callFunctionOn): Promise was collected
W20200612-14:11:02.429(0)? (STDERR) at Promise (/home/jenkins/humly-home/packages/hrm/node_modules/puppeteer/lib/Connection.js:183:56)
...(省略完整栈信息)
Too long with no output (exceeded 1m0s): context deadline exceeded

排查后发现,移除使用Enzyme浅渲染的单元测试后异常就消失,但测试代码本身没发现问题,而且这个异常是偶发的——每2-3次运行才会出现一次。

环境与代码信息

核心依赖版本

  • meteor 1.8.3
  • node 8.17.0
  • puppeteer 1.20.0
  • meteortesting:browser-tests@1.3.1
  • meteortesting:mocha@1.1.3
  • react 16.12.0
  • enzyme 3.10.0

引发问题的典型Enzyme测试代码

import React from "react";
import { expect } from "chai";
import { shallow, configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import ExampleView from "./ExampleView";

configure({ adapter: new Adapter() });

const defaultProps = {
  name: "test name",
  text: "test value",
};

describe(".../ExampleView", () => {
  before(() => {
    // methods stubs
  });

  after(() => {
    // restores
  });

  it("rendering component", () => {
    const props = {
      ...defaultProps,
    };
    const wrapper = shallow(<ExampleView {...props} />);
    const specificElement = wrapper.find(".specific-class-name");
    expect(specificElement.text()).to.be.equal(defaultProps.text);
  });
});

CI测试执行脚本

#!/bin/bash -eu
cd "$(dirname "${BASH_SOURCE[0]}")"/..
export PORT="${PORT:-5000}"
export ROOT_URL="http://localhost:${PORT}"
export TEST_BROWSER_DRIVER=puppeteer
export MOCHA_TIMEOUT=6000
cd node_modules/puppeteer/.local-chromium/linux-*/chrome-linux/
sudo chown root:root chrome_sandbox
sudo chmod 4755 chrome_sandbox
sudo cp -p chrome_sandbox /usr/local/sbin/chrome-devel-sandbox
export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
cd ../../../../../
meteor test --once --driver-package meteortesting:mocha --port "${PORT}" "$@"

已经尝试在最后一条命令添加-- --exit参数强制Mocha测试完成后退出,但没有效果。

问题分析

这个偶发异常大概率是meteortesting:mocha包和Puppeteer的交互冲突导致的:

  • Enzyme浅渲染测试运行在Node环境中,但meteortesting:mocha搭配Puppeteer时会启动浏览器环境,两者的Promise上下文可能存在未被正确清理的情况。
  • 测试完成后,Puppeteer的CDP会话尝试调用函数时,对应的Promise已经被垃圾回收,触发Protocol错误,进而导致Node进程因未处理的Promise拒绝而挂起,最终引发CI超时。

解决方案建议

1. 分离单元测试与浏览器测试

把不需要浏览器的Enzyme单元测试和依赖Puppeteer的浏览器测试分开执行,从根源上避免环境冲突:

  • 给单元测试单独写一个执行脚本,使用meteortesting:mocha的无浏览器模式:
export TEST_BROWSER_DRIVER=none
meteor test --once --driver-package meteortesting:mocha --port "${PORT}" --raw-logs
  • 浏览器测试继续使用原来的Puppeteer驱动脚本,这样两种测试环境互不干扰。

2. 手动清理Puppeteer会话

在全局测试文件(比如tests/setup.js)中添加全局after钩子,手动关闭Puppeteer浏览器实例,确保所有Promise都被正确处理:

import { PuppeteerBrowser } from 'meteortesting:browser-tests';

after(async () => {
  // 强制关闭Puppeteer实例,清理所有会话
  if (PuppeteerBrowser) {
    await PuppeteerBrowser.close();
  }
});

3. 升级兼容的依赖版本

尝试升级相关依赖到更稳定兼容的版本:

  • 把meteortesting:mocha升级到v2.x+版本,该版本对Puppeteer的兼容性有优化
  • 升级Puppeteer到v2.x+版本,修复旧版本中的Promise处理bug
  • 确保Enzyme和React适配器版本完全匹配(当前enzyme 3.10.0建议搭配enzyme-adapter-react-16.11.0)

4. 调整CI资源与超时配置

在Circle CI配置中增加测试步骤的超时时间,同时给容器分配足够的CPU/内存资源——偶发问题有时和资源不足导致的垃圾回收时机异常有关:

# .circleci/config.yml示例片段
jobs:
  test:
    docker:
      - image: circleci/node:8.17.0-browsers
    steps:
      - checkout
      - run: npm install
      - run:
          name: Run Tests
          command: ./scripts/test.sh
          no_output_timeout: 2m # 延长超时时间

总结

优先尝试分离单元测试与浏览器测试的方案,这是最彻底的解决思路;其次可以通过升级依赖、手动清理会话来修复交互问题,同时调整CI资源配置降低偶发概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 08:58:12