如何让Playwright项目在Cmd+C中断时仍执行Teardown(非全局方式)
确保Playwright项目级Teardown在中断时执行的非全局方案
针对Mac上按下Cmd+C(触发SIGINT信号)中断测试时,项目级Teardown无法自动执行的问题,以下是几种不依赖全局Teardown的可行方案:
方案1:监听进程信号,单独执行Teardown项目
直接捕获中断信号,调用Playwright CLI执行指定的Teardown项目,完全复用现有Teardown代码。
实现步骤
- 在项目根目录创建信号处理脚本
signal-handler.js:
const { execSync } = require('child_process'); process.on('SIGINT', async () => { console.log('\n收到中断信号,启动Teardown流程...'); try { // 按顺序执行所有需要的Teardown项目 execSync( 'npx playwright test --project=citizen-users-teardown --project=citizen-auth-teardown', { stdio: 'inherit' } ); process.exit(0); } catch (err) { console.error('Teardown执行失败:', err.message); process.exit(1); } });
- 修改测试启动命令,预先加载该脚本:
node -r ./signal-handler.js node_modules/@playwright/test/cli.js test
优缺点
- 优点:无需修改现有测试/Teardown代码,完全复用已有逻辑
- 缺点:依赖命令行执行,复杂项目中需注意路径和环境变量一致性
方案2:在自定义Fixture中嵌入中断监听与清理逻辑
利用Playwright Fixture的生命周期,将Teardown逻辑绑定到fixture中,确保中断时能直接访问request上下文和自定义fixture。
实现步骤
- 创建包含中断处理的自定义fixture:
import { test as base } from '@playwright/test'; const test = base.extend({ protectedRequest: async ({ request }, use) => { let isTeardownRunning = false; // 封装Teardown逻辑 const runTeardown = async () => { if (isTeardownRunning) return; isTeardownRunning = true; console.log('\n触发中断,执行Fixture内Teardown'); // 替换为你的实际清理逻辑 await request.delete('/api/users/test-citizen'); await request.delete('/api/auth/sessions/test-session'); process.exit(0); }; // 监听中断和终止信号 process.on('SIGINT', runTeardown); process.on('SIGTERM', runTeardown); // 传递request上下文给测试 await use(request); // 测试正常结束后移除监听 process.off('SIGINT', runTeardown); process.off('SIGTERM', runTeardown); }, }); export default test;
- 在需要依赖Setup的测试中使用该fixture:
import test from '../fixtures/protected-request'; test('full functional test', async ({ protectedRequest }) => { // 测试逻辑 });
优缺点
- 优点:直接集成到Playwright生态,可无缝访问所有fixture依赖
- 缺点:需将原Teardown项目的逻辑迁移到fixture中,存在一定代码重构成本
方案3:在Setup测试文件中结合test.afterAll与信号监听
在每个Setup文件内封装Teardown逻辑,同时监听中断信号触发清理,确保正常结束和中断时都能执行。
实现步骤
以citizen-auth.setup.ts为例:
import { test } from '@playwright/test'; // 封装共享的清理逻辑 async function cleanUpAuth(request) { console.log('执行Auth资源清理'); await request.delete('/api/auth/test-sessions'); } // 监听中断信号,触发时创建新的request上下文执行清理 process.on('SIGINT', async () => { const tempRequest = await test.request.newContext(); await cleanUpAuth(tempRequest); await tempRequest.dispose(); process.exit(0); }); test('setup citizen auth', async ({ request }) => { // 原Setup逻辑 await request.post('/api/auth/register', { data: { username: 'test-citizen' } }); }); // 正常测试结束时执行清理 test.afterAll(async ({ request }) => { await cleanUpAuth(request); });
优缺点
- 优点:无需额外脚本,修改集中在Setup文件内
- 缺点:需在每个Setup文件中重复添加监听逻辑,信号触发时需重新创建上下文
内容的提问来源于stack exchange,提问作者kda_100
相关产品推荐
相关产品推荐

