Electron 调试与测试

三类调试链路、单元测试、端到端测试与常见崩溃排查。

Advanced
Key Points渲染进程用 DevTools,主进程用 --inspect,整应用用 Playwright 做端到端——三层各管一段。

三层调试

LayerToolWhat it shows
RendererChrome DevToolsDOM、Console、Network、Performance
Main--inspect + 调试器断点、调用栈、变量
Whole appPlaywright / Spectron真实点击、窗口、文件结果

单元测试

JavaScript
// 纯逻辑抽成独立模块,不依赖 electron,就能直接单测
// lib/format.js
function formatBytes(n) {
  const units = ['B', 'KB', 'MB', 'GB'];
  let i = 0;
  while (n >= 1024 && i < units.length - 1) { n /= 1024; i++; }
  return `${n.toFixed(1)} ${units[i]}`;
}
module.exports = { formatBytes };
JavaScript
// test/format.test.js
const { test } = require('node:test');
const assert = require('node:assert');
const { formatBytes } = require('../lib/format');

test('formatBytes', () => {
  assert.equal(formatBytes(512), '512.0 B');
  assert.equal(formatBytes(2048), '2.0 KB');
  assert.equal(formatBytes(5 * 1024 * 1024), '5.0 MB');
});
Bash
node --test
Output
✔ formatBytes (1.2ms)
ℹ tests 1
ℹ pass 1
ℹ fail 0

Electron code is hard to test because require('electron') yields nothing real under plain Node. Extract logic into modules that never import electron and let the main process only wire things up — then most logic is covered by ordinary unit tests.

端到端测试

JavaScript
// e2e/app.spec.js —— Playwright 支持直接启动 Electron
const { test, expect, _electron: electron } = require('@playwright/test');

test('窗口能打开并显示标题', async () => {
  const app = await electron.launch({ args: ['.'] });
  const win = await app.firstWindow();

  await expect(win).toHaveTitle(/Hello/);
  await win.click('#btn');
  await expect(win.locator('#info')).toContainText('Electron');

  await app.close();
});

electron.launch starts the real app and can verify the whole click → IPC → main → file chain. Unit tests cover one link; only end-to-end tests cover the wiring.

常见崩溃排查

SymptomCause
White windowloadFile 路径错,或渲染进程抛异常
App exits silently主进程未捕获异常
NODE_MODULE_VERSION mismatch原生模块没按 Electron 重编译
Blank after packagingfiles 漏了资源,或用了 __dirname 相对路径
Crash on GPU显卡驱动问题,可 app.disableHardwareAcceleration()
JavaScript
// 兜住主进程的未捕获异常,至少写进日志
process.on('uncaughtException', (err) => {
  console.error('主进程未捕获异常', err);
  logToFile(err);
});

process.on('unhandledRejection', (reason) => {
  console.error('未处理的 Promise 拒绝', reason);
});
A blank window after packaging is almost always a resource path issue: __dirname points into app.asar after packaging, so relative bases change. Use path.join(__dirname, ...) and confirm files includes those assets.

Friends & Partners