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

Playwright测试偶发失败原因及稳定化方案咨询

Playwright测试用例偶发失败排查与修复

问题背景

我有一组Playwright测试用例,存在偶发失败的情况(原问题基于开发中网站,已适配公开URL复现)。文件结构如下:

├── Dockerfile
├── package.json
├── tests
│   ├── 00.spec.ts
│   ├── 01.spec.ts
│   ├── 02.spec.ts
│   ├── 03.spec.ts
│   ├── 04.spec.ts

其中tests/00.spec.ts整合了01-04的测试用例,因此4个测试场景各会运行两次(一次在00.spec.ts,一次在对应0x.spec.ts)。

执行命令:

docker build -t playwright-test . && docker run --rm -it playwright-test

连续执行10次的结果:

  1. 全部成功
  2. 04失败,其余成功
  3. 全部成功
  4. 00(league form 1)失败,其余成功
  5. 04失败,其余成功
  6. 03失败,其余成功
  7. 02失败,其余成功
  8. 00(league form 1)失败,其余成功
  9. 全部成功
  10. 全部成功

失败错误信息

测试失败时抛出超时错误,日志如下:

1) tests/00.spec.ts:54:7 › league form 1 ────────────────────────────────────────────────────────

    Test timeout of 30000ms exceeded.

    Error: locator.click: Test timeout of 30000ms exceeded.
    Call log:
      - waiting for locator('#news-sidebar .sidebar-teaser form').locator('.form-wrapper').locator('.select-wrapper').nth(5).locator('a').filter({ hasText: /^Deutschland$/ })
      -   locator resolved to <a class="ng-binding" data-ng-click="wamData.areas.…>Deutschland</a>
      - attempting click action
      -   waiting for element to be visible, enabled and stable
      -     element is not visible - waiting...


      58 |     for (const div of await form_wrapper.locator('.select-wrapper').all()) {
      59 |       await div.locator('span.icon-angle-down').click(); // show dropdown options
    > 60 |       await div.locator('a', {hasText: new RegExp('^' + leagues[url][i] + '$') }).click(); // select option
         |                                                                                   ^
      61 |       i++;
      62 |     }
      63 |     await form.locator('button:has-text("anzeigen")').click();

        at /usr/src/app/tests/00.spec.ts:60:83

原因分析

从错误日志和测试行为来看,偶发失败的核心原因是交互时机不对,具体包括:

  • 点击下拉按钮后,下拉选项未完全渲染/展开就尝试点击,导致Playwright等待元素稳定超时
  • Cookie弹窗处理不严谨:未等待弹窗元素完全可见就执行点击,可能导致后续页面交互受影响
  • DOM动态变化影响:一次性获取所有.select-wrapper元素后遍历,若页面在交互过程中DOM发生变化,会导致已获取的元素引用失效
  • Playwright版本不兼容:Docker镜像使用v1.41.1,但package.json依赖的@playwright/test是v1.16.1,版本差异可能导致API行为不一致

解决方案

1. 同步Playwright版本

Docker镜像和package.json中的Playwright版本保持一致,避免兼容性问题:

  • 修改package.json的@playwright/test版本为^1.41.1
  • Dockerfile保持原镜像版本即可

2. 优化Cookie弹窗处理

等待弹窗元素可见后再执行点击,确保操作时机正确:

test.beforeEach(async ({ page }) => {
  await page.goto('http://www.fussball.de');

  // 等待Cookie弹窗加载完成并可见
  const ucr = page.locator('#usercentrics-root');
  await ucr.waitFor({ state: 'visible' });
  await ucr.locator('button:has-text("Akzeptiere alle")').click();
  // 等待弹窗消失,避免遮挡后续元素
  await ucr.waitFor({ state: 'hidden' });
});

3. 优化下拉选项点击逻辑

点击下拉按钮后,等待目标选项可见再执行点击,确保元素处于可交互状态:

for (let i = 0; i < leagues[url].length; i++) {
  // 按索引定位下拉框,避免DOM变化导致的元素失效
  const selectWrapper = form_wrapper.locator('.select-wrapper').nth(i);
  await selectWrapper.locator('span.icon-angle-down').click();
  
  // 等待目标选项可见后再点击
  const targetOption = selectWrapper.locator('a', { 
    hasText: new RegExp('^' + leagues[url][i] + '$') 
  });
  await targetOption.waitFor({ state: 'visible' });
  await targetOption.click();
}

4. 调整Playwright配置(可选)

在playwright.config.ts中增加超时时间或调整重试策略,应对极端慢加载场景:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  timeout: 60000, // 全局超时调整为60秒
  retries: 2, // 失败测试自动重试2次
  use: {
    actionTimeout: 30000, // 单个操作超时
    baseURL: 'http://www.fussball.de',
  },
});

优化后代码示例(以00.spec.ts为例)

import { test, expect } from '@playwright/test';

test.beforeEach(async ({ page }) => {
  await page.goto('/');

  // 处理Cookie弹窗
  const ucr = page.locator('#usercentrics-root');
  await ucr.waitFor({ state: 'visible' });
  await ucr.locator('button:has-text("Akzeptiere alle")').click();
  await ucr.waitFor({ state: 'hidden' });
});
  
const leagues = {
  'https://www.fussball.de/spieltagsuebersicht/a-junioren-bundesliga-west-deutschland-a-junioren-bundesliga-a-junioren-saison2324-deutschland/-/staffel/02LNP13DU800000FVS5489B4VUAB0UC4-G#!/': [
    'Deutschland',
    '23/24',
    'Meisterschaften',
    'A-Junioren',
    'Bundesliga',
    'Deutschland',
    'A-Junioren-Bundesliga West'
  ],
  'https://www.fussball.de/spieltagsuebersicht/bfv-landesliga-odenwald-baden-landesliga-herren-saison2324-baden/-/staffel/02M5LMHKRK00000FVS5489B4VSAUO6GA-G#!/': [
    'Baden',
    '23/24',
    'Meisterschaften',
    'Herren',
    'Landesliga',
    'Baden',
    'bfv-Landesliga Odenwald'
  ],
  'https://www.fussball.de/spieltagsuebersicht/landesliga-mitte-bayern-landesliga-herren-saison2324-bayern/-/staffel/02LS70U7C4000007VS5489B4VUAB0UC4-G#!/': [
    'Bayern',
    '23/24',
    'Meisterschaften',
    'Herren',
    'Landesliga',
    'Bayern',
    'Landesliga Mitte'
  ],
  'https://www.fussball.de/spieltagsuebersicht/herren-landesliga-st1-berlin-landesliga-herren-saison2324-berlin/-/staffel/02M7480S2O000006VS5489B4VSAUO6GA-G#!/': [
    'Berlin',
    '23/24',
    'Meisterschaften',
    'Herren',
    'Landesliga',
    'Berlin',
    'Herren Landesliga St.1'
  ],
};

let j = 1;
for (const url of Object.keys(leagues)) {
  test(`league form ${j}`, async ({ page }) => {
    const form = page.locator('#news-sidebar .sidebar-teaser form');
    const form_wrapper = form.locator('.form-wrapper');
    
    for (let i = 0; i < leagues[url].length; i++) {
      const selectWrapper = form_wrapper.locator('.select-wrapper').nth(i);
      await selectWrapper.locator('span.icon-angle-down').click();
      
      const targetOption = selectWrapper.locator('a', { 
        hasText: new RegExp(`^${leagues[url][i]}$`) 
      });
      await targetOption.waitFor({ state: 'visible' });
      await targetOption.click();
    }
    
    await form.locator('button:has-text("anzeigen")').click();
    await expect(page).toHaveURL(url);
  });

  j++;
}

修改后的package.json片段

{
  "devDependencies": {
    "@playwright/test": "^1.41.1",
    "jest": "^27.3.1"
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 08:53:10