使用 exec('taskkill /F /IM notepad.exe')
可以直接强制关闭所有正在运行的 notepad.exe
进程。然而,这种方法有其优缺点
a. 管理进程句柄
为了更精确地控制应用程序的启动和关闭,建议使用 child_process
模块的 spawn
或 exec
方法来启动应用程序
const { spawn } = require('child_process');
const { chromium } = require('playwright');
let appProcess;
async function startApp() {
// 启动应用程序(例如 Notepad)
appProcess = spawn('notepad.exe');
// 等待应用程序启动
await new Promise(resolve => setTimeout(resolve, 1000));
}
async function stopApp() {
if (appProcess) {
appProcess.kill('SIGTERM'); // 优雅关闭
// 如果需要强制终止,可以使用 'SIGKILL'
// appProcess.kill('SIGKILL');
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
describe('Desktop Application UI Test', function() {
beforeEach(async function() {
await startApp();
});
afterEach(async function() {
await stopApp();
});
it('Test Case 1', async function() {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
// 执行测试用例的 UI 自动化操作
// ...
await browser.close();
});
it('Test Case 2', async function() {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
// 执行第二个测试用例的 UI 自动化操作
// ...
await browser.close();
});
});
,并保存进程句柄,以便在测试结束后仅关闭特定的进程。+
b. 使用唯一标识启动的进程
如果必须使用 taskkill
,可以通过为每个测试用例启动的应用程序分配唯一标识来选择性地关闭特定的进程。例如,可以在启动应用程序时传递特定参数或使用特定工作目录,以便后续识别和关闭。
const { exec, spawn } = require('child_process');
let appProcess;
async function startApp() {
// 使用特定参数启动应用程序
appProcess = spawn('notepad.exe', ['-someUniqueParam']);
await new Promise(resolve => setTimeout(resolve, 1000));
}
async function stopApp() {
if (appProcess) {
exec(`taskkill /F /PID ${appProcess.pid}`);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
c. 检查进程状态
在关闭进程后,最好检查进程是否已成功终止,以确保测试环境的清理工作已完成。
const psList = require('ps-list');
async function isAppRunning(appName) {
const processes = await psList();
return processes.some(p => p.name.toLowerCase() === appName.toLowerCase());
}
async function waitForAppClose(appName, timeout = 5000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
if (!(await isAppRunning(appName))) {
return true;
}
await new Promise(resolve => setTimeout(resolve, 500));
}
throw new Error(`${appName} did not close in time`);
}
// 使用示例
async function stopApp() {
exec('taskkill /F /IM notepad.exe');
await waitForAppClose('notepad.exe');
}
d. 优雅关闭应用程序
如果应用程序支持通过命令行参数、IPC 或其他方式进行优雅关闭,优先使用这些方法,以确保数据完整性和资源正确释放。
// 假设应用程序支持通过特定端口或接口接收关闭命令
const axios = require('axios');
async function stopAppGracefully() {
try {
await axios.post('http://localhost:PORT/shutdown');
} catch (error) {
console.error('Failed to gracefully shutdown the app:', error);
// 可以在失败时选择性地强制终止
exec('taskkill /F /IM notepad.exe');
}
}
const { exec } = require('child_process');
// 强制关闭所有 notepad.exe 进程
exec('taskkill /F /IM notepad.exe', (err, stdout, stderr) => {
if (err) {
console.error(`Error closing notepad: ${stderr}`);
} else {
console.log('All notepad instances closed successfully.');
}
});