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

如何配置Playwright仅对测试所需用户执行多角色认证?

如何让Playwright仅认证测试所需的用户?

问题场景

我按照Playwright多角色认证的方案实现了多用户认证、状态保存与复用,但遇到一个效率问题:当测试用户数量较多(比如100个)时,即便只运行某部分测试(如仅标注Smoke的冒烟测试),Playwright仍会先对所有用户完成认证,再执行目标测试。而冒烟测试只用到其中个别用户,全量认证完全没必要。

现有认证逻辑代码:

import { test as setup, expect } from '@playwright/test';
import * as data from '@/source/testData/data.json';

const keys = Object.keys(data.users);
keys.forEach((key) => {
  const credentials = data.users[key as keyof typeof data.users];
  const userFile = `playwright/.auth/${credentials.username}.json`;

  setup(`Authenticate user ${credentials.username}`, async ({ page }) => {
    await page.goto('https://somerandomwebsite.com');
    await page.getByLabel('Username').fill(credentials.username);
    await page.getByLabel('Password').fill(credentials.password);
    await page.click('text=Submit');

    await expect(page.getByRole('button', { name: 'Profile' })).toBeVisible({ timeout: 30000 });
    await page.context().storageState({ path: userFile });
  });
});

测试中使用认证状态的方式:

test.describe('Smoke tests', () => {
  test.use({ storageState: 'playwright/.auth/customer1.json' });

  test('add item to cart', async ({ page }) => {
    // 测试逻辑
  });
});

解决方案

方法一:通过环境变量指定目标用户

运行测试时通过环境变量传递需要认证的用户名,让setup脚本仅处理这些用户,同时保留兼容性(未指定时默认全量认证)。

修改后的setup代码:

import { test as setup, expect } from '@playwright/test';
import * as data from '@/source/testData/data.json';
import fs from 'fs';

// 从环境变量获取需要认证的用户(支持逗号分隔多个用户)
const targetUsers = process.env.TARGET_USERS ? process.env.TARGET_USERS.split(',') : [];

// 确定要认证的用户列表:指定了用户则只处理指定的,否则处理所有
const usersToAuth = targetUsers.length > 0 
  ? targetUsers.map(username => data.users[username as keyof typeof data.users])
  : Object.values(data.users);

// 过滤掉不存在的用户,避免报错
const validUsers = usersToAuth.filter(user => !!user);
if (validUsers.length !== targetUsers.length) {
  const missingUsers = targetUsers.filter(u => !data.users[u as keyof typeof data.users]);
  console.warn(`Warning: Users ${missingUsers.join(',')} not found in data.json`);
}

validUsers.forEach((credentials) => {
  const userFile = `playwright/.auth/${credentials.username}.json`;

  setup(`Authenticate user ${credentials.username}`, async ({ page }) => {
    // 先检查状态文件是否存在,存在则跳过认证
    if (fs.existsSync(userFile)) {
      console.log(`Skipping authentication for ${credentials.username} (state file exists)`);
      return;
    }

    await page.goto('https://somerandomwebsite.com');
    await page.getByLabel('Username').fill(credentials.username);
    await page.getByLabel('Password').fill(credentials.password);
    await page.click('text=Submit');

    await expect(page.getByRole('button', { name: 'Profile' })).toBeVisible({ timeout: 30000 });
    await page.context().storageState({ path: userFile });
  });
});

运行冒烟测试时,只需指定目标用户:

TARGET_USERS=customer1 npx playwright test --grep "Smoke"

方法二:自动检测测试中用到的用户

编写脚本扫描测试文件,提取所有用到的storageState路径,解析出对应的用户名,让setup仅认证这些用户,无需手动指定。

修改后的setup代码(包含扫描逻辑):

import { test as setup, expect } from '@playwright/test';
import * as data from '@/source/testData/data.json';
import fs from 'fs';
import path from 'path';

// 扫描测试目录,提取所有用到的用户名
function getUsedUsers(testDir: string): string[] {
  const usedUsernames = new Set<string>();
  // 匹配storageState路径的正则
  const statePathRegex = /storageState: ['"]playwright\/\.auth\/(.*)\.json['"]/g;

  // 递归扫描目录
  function scanDirectory(dir: string) {
    const entries = fs.readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      if (entry.isDirectory()) {
        scanDirectory(fullPath);
      } else if (entry.isFile() && (fullPath.endsWith('.spec.ts') || fullPath.endsWith('.spec.js'))) {
        const content = fs.readFileSync(fullPath, 'utf8');
        let match;
        while ((match = statePathRegex.exec(content)) !== null) {
          usedUsernames.add(match[1]);
        }
      }
    }
  }

  scanDirectory(testDir);
  return Array.from(usedUsernames);
}

// 获取当前测试用到的用户
const usedUsers = getUsedUsers('./tests'); // 替换为你的测试目录路径
const usersToAuth = usedUsers.map(username => data.users[username as keyof typeof data.users]).filter(user => !!user);

usersToAuth.forEach((credentials) => {
  const userFile = `playwright/.auth/${credentials.username}.json`;

  setup(`Authenticate user ${credentials.username}`, async ({ page }) => {
    if (fs.existsSync(userFile)) {
      console.log(`Skipping authentication for ${credentials.username} (state file exists)`);
      return;
    }

    await page.goto('https://somerandomwebsite.com');
    await page.getByLabel('Username').fill(credentials.username);
    await page.getByLabel('Password').fill(credentials.password);
    await page.click('text=Submit');

    await expect(page.getByRole('button', { name: 'Profile' })).toBeVisible({ timeout: 30000 });
    await page.context().storageState({ path: userFile });
  });
});

这种方式会自动识别当前要运行的测试中用到的所有用户,无需额外参数,适合不想手动管理用户列表的场景。

方法三:利用Playwright Fixture的依赖机制

将每个用户的认证逻辑封装为fixture,测试中明确依赖对应的fixture,Playwright会自动仅运行被依赖的fixture(即仅认证测试用到的用户)。

首先创建fixture文件:

// fixtures.ts
import { test as base } from '@playwright/test';
import * as data from '@/source/testData/data.json';
import fs from 'fs';

// 定义用户fixture类型
type UserFixtures = { [K in keyof typeof data.users]: void };

// 动态生成所有用户的fixture
const userFixtures: Partial<UserFixtures> = {};

Object.entries(data.users).forEach(([userKey, credentials]) => {
  userFixtures[userKey as keyof UserFixtures] = [async ({ page }, use) => {
    const userFile = `playwright/.auth/${credentials.username}.json`;
    
    // 状态文件不存在时才执行认证
    if (!fs.existsSync(userFile)) {
      await page.goto('https://somerandomwebsite.com');
      await page.getByLabel('Username').fill(credentials.username);
      await page.getByLabel('Password').fill(credentials.password);
      await page.click('text=Submit');

      await expect(page.getByRole('button', { name: 'Profile' })).toBeVisible({ timeout: 30000 });
      await page.context().storageState({ path: userFile });
    }

    await use();
  }, { scope: 'worker' }]; // 设置为worker级别,每个worker只认证一次
});

// 扩展基础test,加入用户fixtures
export const test = base.extend<UserFixtures>(userFixtures);

然后在测试中使用:

import { test } from './fixtures';

test.describe('Smoke tests', () => {
  test.use({ storageState: 'playwright/.auth/customer1.json' });

  // 依赖customer1的fixture,触发该用户的认证(如果状态文件不存在)
  test('add item to cart', async ({ page, customer1 }) => {
    // 测试逻辑
  });
});

这种方式完全贴合Playwright的测试范式,且能自动管理认证逻辑的执行时机,仅为被测试用到的用户执行认证。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 08:35:56