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

Playwright认证状态返回空Cookies数组,关联测试失败求助

问题:Playwright共享认证导出的cookies为空导致测试失败

我使用Playwright实现测试用共享账号认证,但导出的认证状态中cookies数组为空,导致依赖认证的后续测试无法正常运行。当前使用的@playwright/test版本为^1.45.1。

导出的认证状态内容如下:

{
  "cookies": [],
  "origins": [
    {
      "origin": "http://localhost:3000",
      "localStorage": [
        {
          "name": "locale",
          "value": "en_US"
        }
      ]
    }
  ]
}

我的Playwright配置文件、认证setup脚本、测试脚本分别如下:

配置文件

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

// Read from ".env" file.
dotenv.config({ path: path.resolve(__dirname, ".env") });

/**
 * See https://playwright.dev/docs/test-configuration.
 */
export default defineConfig({
  testDir: './tests',
  /* Run tests in files in parallel */
  fullyParallel: true,
  /* Fail the build on CI if you accidentally left test.only in the source code. */
  forbidOnly: !!process.env.CI,
  /* Retry on CI only */
  retries: process.env.CI ? 2 : 0,
  /* Opt out of parallel tests on CI. */
  workers: process.env.CI ? 1 : undefined,
  /* Reporter to use. See https://playwright.dev/docs/test-reporters */
  reporter: 'html',
  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
  use: {
    /* Base URL to use in actions like `await page.goto('/')`. */
    // baseURL: 'http://127.0.0.1:3000',

    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
    trace: 'on-first-retry',
  },

  /* Configure projects for major browsers */
  projects: [
    {
      name: 'setup',
      testMatch: 'tests/auth-setup.ts',
    },
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: 'playwright/.auth/user.json',
       },
       dependencies: ['setup'],
    },

    // {
    //   name: 'firefox',
    //   use: { ...devices['Desktop Firefox'] },
    // },

    // {
    //   name: 'webkit',
    //   use: { ...devices['Desktop Safari'] },
    // },

    /* Test against mobile viewports. */
    // {
    //   name: 'Mobile Chrome',
    //   use: { ...devices['Pixel 5'] },
    // },
    // {
    //   name: 'Mobile Safari',
    //   use: { ...devices['iPhone 12'] },
    // },

    /* Test against branded browsers. */
    // {
    //   name: 'Microsoft Edge',
    //   use: { ...devices['Desktop Edge'], channel: 'msedge' },
    // },
    // {
    //   name: 'Google Chrome',
    //   use: { ...devices['Desktop Chrome'], channel: 'chrome' },
    // },
  ],

  /* Run your local dev server before starting the tests */
  // webServer: {
  //   command: 'npm run start',
  //   url: 'http://127.0.0.1:3000',
  //   reuseExistingServer: !process.env.CI,
  // },
});

认证setup脚本

import { test as setup, expect, chromium, Browser, Page } from '@playwright/test';

const authFile = 'playwright/.auth/user.json';

setup('authenticate user', async ({ }) => {
  const browser: Browser = await chromium.launch({headless: true});
  const context = await browser.newContext();
  const page: Page = await context.newPage();

  await page.goto(`${process.env.APP_BASE_URL}/login`);
  await page.locator('input[name="username"]').fill(`${process.env.APP_USERNAME}`);
  await page.locator('input[name="password"]').fill(`${process.env.APP_PASSWORD}`);
  await page.locator("button[aria-label='confirmButton']").click();

  await page.waitForURL(`${process.env.APP_BASE_URL}/accounts#operations`);

  await expect(page.getByText('Available balance')).toHaveText('Available balance');

  await page.context().storageState({ path: authFile });
});

测试脚本

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

test('display accounts page', async ({ page }) => {
  await page.goto(`${process.env.APP_BASE_URL}/accounts#operations`);
  await page.waitForTimeout(5000);
  await expect(page.getByText('Available balance')).toHaveText('Available balance');
  await page.waitForTimeout(5000);
  await page.screenshot({
    path: "./playwright/screenshots/screen-capture.png",
  });
});

setup测试执行成功,但业务测试失败,截图显示停留在登录页。此前按教程操作时cookies有正常内容,测试可正常运行。

可能导致cookies为空的原因
  • 认证上下文不匹配:你的setup脚本手动创建了浏览器和上下文,和Playwright测试框架内置的上下文完全分离。手动创建的上下文可能没有被正确捕获状态,或者框架无法识别这个上下文的认证信息。应该直接使用setup测试提供的page或context对象,而非手动启动浏览器。
  • Cookie属性与URL不匹配:如果应用设置的Cookie域名、路径和APP_BASE_URL不一致,比如Cookie绑定的是127.0.0.1但你的URL用的是localhost,或者路径限制为特定路由,Playwright无法导出这些不匹配的Cookie。
  • 认证完成后等待不充分:虽然你添加了页面跳转和文本断言,但Cookie可能还未完全写入浏览器。可以等待认证接口的响应完成,或者直接等待目标认证Cookie出现后再导出状态。
  • Headless模式差异:手动启动浏览器时指定了headless: true,这是Playwright的旧版无头模式,而v1.38+默认使用新版无头模式。两种模式的Cookie存储行为可能存在差异,尝试改用headless: 'new'或直接使用框架默认的上下文。
  • 存储状态调用对象错误:脚本中用page.context().storageState(),如果页面跳转后切换了上下文(比如打开了新窗口),page.context()可能不是实际完成认证的上下文。应该直接使用你创建的context对象来调用storageState。
  • 协议不一致:如果登录过程中HTTP和HTTPS协议切换,Cookie可能被限制在对应协议下,导出的状态无法跨协议携带。确保APP_BASE_URL的协议和应用实际运行的协议完全一致。
修复示例

修改setup脚本,使用测试内置的上下文,避免手动启动浏览器:

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

const authFile = 'playwright/.auth/user.json';

setup('authenticate user', async ({ page, context }) => {
  await page.goto(`${process.env.APP_BASE_URL}/login`);
  await page.locator('input[name="username"]').fill(`${process.env.APP_USERNAME}`);
  await page.locator('input[name="password"]').fill(`${process.env.APP_PASSWORD}`);
  await page.locator("button[aria-label='confirmButton']").click();

  await page.waitForURL(`${process.env.APP_BASE_URL}/accounts#operations`);
  await expect(page.getByText('Available balance')).toHaveText('Available balance');

  // 等待目标认证Cookie出现,替换为你的实际Cookie名称
  await context.waitForCookie({ name: 'auth_token' });

  await context.storageState({ path: authFile });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 00:10:54