Playwright测试触发403错误但手动操作正常的排查求助
生产环境中Playwright自动化测试交互按钮失败,手动操作正常的问题排查
问题描述
在生产环境网站中,Playwright自动化测试点击注册、加入购物车等交互按钮时失败,但手动执行相同步骤可正常工作。例如注册新用户的测试始终失败,手动注册却无异常。
测试执行步骤:
- 访问网站
- 点击用户图标
- 在注册弹窗中输入待创建的用户邮箱
- 点击Continue按钮
现象详情
- 网站UI提示:
An error ocurred, please try again - 控制台报错:
Failed to load resource: the server responded with a status of 403 /api/account/profile/auth-options:1 - 网络面板显示:
auth-options接口请求返回403 Forbidden,提示无访问权限 - 测试因登录函数调用后超时失败
测试代码与配置
测试代码
import { test, expect } from '@playwright/test'; import { LandingPage } from '../pageObjects/landingPage'; import { LoginModalPage } from '../pageObjects/loginModalPage'; test.describe('test suite', () => { test.beforeEach(async({ page }) => { const landingPage = new LandingPage(page); await landingPage.goToLandingPage(); }); test('add product to the cart', async ({ page }) => { const landingPage = new LandingPage(page); const loginModal = new LoginModalPage(page); await landingPage.customerInfoButton.click(); await loginModal.login('test@test.com', 'password'); await landingPage.logout(); }); });
Page Object代码
LoginModalPage
import { Page, Locator } from '@playwright/test' export class LoginModalPage { readonly page: Page; readonly loginModal: Locator; readonly emailInputField: Locator; readonly contactDataCheckbox: Locator; readonly termsAndConditionsCheckbox: Locator; readonly continueButton: Locator; constructor(page: Page){ this.page = page; this.loginModal = page.getByTestId('landing-screen'); this.emailInputField = this.loginModal.getByRole('textbox', {name: 'email address'}); this.contactDataCheckbox = this.loginModal.locator('.gl-checkbox__label', {hasText: 'contact data'}); this.termsAndConditionsCheckbox = this.loginModal.locator('.gl-checkbox__label', {hasText: 'Register'}); this.continueButton = this.loginModal.getByRole('button', {name: 'Continue'}); } async login(user: string, password: string){ await this.emailInputField.pressSequentially(user, {delay: 50}); await this.contactDataCheckbox.click(); await this.termsAndConditionsCheckbox.click(); await this.continueButton.click(); } }
LandingPage
import { Page, expect, Locator } from '@playwright/test' export class LandingPage { readonly page: Page; readonly cookiesButton: Locator; readonly heading: Locator; readonly customerInfoButton: Locator; readonly accountTab: Locator; constructor(page: Page){ this.page = page; this.cookiesButton = page.getByRole('button', { name: 'accept cookies'}); this.heading = page.getByRole('heading', { name: 'buy now' }); this.customerInfoButton = page.getByTestId('customer-info-button'); this.accountTab = page.getByTestId('home-account-tab'); } async goToLandingPage(){ await this.page.goto('/'); } async checkTitleAndHeaderPage(){ await expect(this.page).toHaveTitle(/official site/); await expect(this.heading).toBeVisible(); } }
Playwright配置
import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests', fullyParallel: false, retries: process.env.CI ? 2 : 2, workers: process.env.CI ? 1 : undefined, reporter: 'html', use: { baseURL: websiteURL, testIdAttribute: 'data-auto-id', trace: 'on', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, { name: 'Google Chrome', use: { ...devices['Desktop Chrome'], channel: 'chrome' }, }, ], });
已尝试的解决方法
- 更换测试输入数据
- 在不同浏览器中运行测试
- 添加硬等待
- 清理Cookie(未生效)
- 在代码中设置User Agent
- 对比测试与手动请求的Header差异:
- Referrer Policy:测试中为
no-referrer,手动操作为strict-origin-when-cross-origin - content-type响应头:测试中为
text/html,手动操作为application/json; charset=utf-8 - Referer:测试中为空,手动操作为对应URL/endpoint
- Sec-Ch-Ua、Sec-Ch-Ua-Platform、User-Agent字段均存在差异
- Referrer Policy:测试中为
解决建议
对齐请求头参数
- 在Playwright页面初始化时设置正确的Referrer Policy:
await page.setExtraHTTPHeaders({ 'Referrer-Policy': 'strict-origin-when-cross-origin' }); - 拦截
auth-options接口请求,修正关键头部信息:await page.route('/api/account/profile/auth-options', async (route) => { const originalHeaders = route.request().headers(); await route.continue({ headers: { ...originalHeaders, 'Referer': 'https://你的生产域名/', // 替换为手动请求的Referer值 'Content-Type': 'application/json; charset=utf-8' } }); });
- 在Playwright页面初始化时设置正确的Referrer Policy:
完全匹配浏览器标识
- 复制手动操作时的完整User-Agent字符串,配置到Playwright中:
// 在playwright.config.js的use字段添加 userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36' - 若Sec-Ch系列头部仍不匹配,通过初始化脚本模拟:
await page.addInitScript(() => { Object.defineProperty(navigator, 'userAgentData', { value: { brands: [{brand: 'Chromium', version: '118'}, {brand: 'Google Chrome', version: '118'}, {brand: 'Not=A?Brand', version: '99'}], platform: 'Windows' } }); });
- 复制手动操作时的完整User-Agent字符串,配置到Playwright中:
完善页面状态处理
- 补充Cookie授权步骤,确保测试环境与手动操作一致:
test.beforeEach(async({ page }) => { const landingPage = new LandingPage(page); await landingPage.goToLandingPage(); await landingPage.cookiesButton.click(); // 新增接受Cookie操作 }); - 点击按钮前等待元素就绪,替换硬等待为自动等待:
async login(user: string, password: string){ await this.emailInputField.pressSequentially(user, {delay: 50}); await this.contactDataCheckbox.click(); await this.termsAndConditionsCheckbox.click(); await expect(this.continueButton).toBeEnabled(); // 等待按钮可用 await this.continueButton.click(); }
- 补充Cookie授权步骤,确保测试环境与手动操作一致:
同步会话状态
- 导出手动操作时的localStorage数据,测试前注入页面:
const localStorageData = require('./localStorageData.json'); // 本地存储的手动会话数据 await page.addInitScript((data) => { Object.keys(data).forEach(key => localStorage.setItem(key, data[key])); }, localStorageData);
- 导出手动操作时的localStorage数据,测试前注入页面:
内容的提问来源于stack exchange,提问作者nZa
相关产品推荐
相关产品推荐

