Playwright数据驱动测试:从Excel与JSON获取测试数据指南

简介: 本文介绍了Playwright中数据驱动测试的实现,重点讲解如何从Excel和JSON文件分离测试数据与逻辑。通过具体代码示例展示了读取工具类的编写与测试用例的应用,比较了两者在维护性、版本控制和适用场景上的优缺点。数据驱动能显著提升代码复用性、降低维护成本,使测试更灵活高效,是优化自动化测试框架的重要实践。

如果你还在为每个测试用例硬编码数据而头疼,或者每次数据变更都要翻遍几十个测试文件——是时候了解数据驱动测试了。今天,我们聊聊如何用 Playwright 优雅地从 Excel 和 JSON 文件中读取测试数据,让你的测试代码真正实现“一次编写,到处运行”。

为什么需要数据驱动测试?

先看个反例。假设我们要测试一个登录功能,传统写法可能是:

test('用户登录测试', async ({ page }) => {
await page.fill('#username', 'zhangsan');
await page.fill('#password', '123456');
await page.click('#login-btn');
// 断言...
});
test('管理员登录测试', async ({ page }) => {
await page.fill('#username', 'admin');
await page.fill('#password', 'admin@123');
await page.click('#login-btn');
// 断言...
});

发现问题了吗?每增加一个测试账户,就要复制粘贴一整段代码。当密码策略变化时,你得修改所有相关测试文件。这种维护成本,你懂的。

而数据驱动测试的思想很简单:分离测试逻辑与测试数据。我们的目标是把上面的代码改造成这样:

// 测试逻辑只有一份
test('登录功能测试', async ({ page }) => {
  const testData = getTestData(); // 从外部文件读取
  for (const data of testData) {
    await performLogin(page, data);
    // 断言...
  }
});

接下来,我们看看具体怎么实现。

实战一:从 Excel 读取测试数据

Excel 可能是产品经理和业务人员最喜欢的数据格式。如果你的测试数据需要经常让非技术人员维护,Excel 是个不错的选择。

第一步:准备测试数据

创建一个 testdata.xlsx 文件,内容如下:

测试场景 username password expected_result
普通用户登录 zhangsan 123456 登录成功
管理员登录 admin admin@123 跳转管理后台
密码错误 lisi wrong_pwd 提示密码错误
用户不存在 notexists 123456 提示用户不存在

保存到项目目录的 data/ 文件夹下。

第二步:安装必要的包

Playwright 本身不处理 Excel,我们需要借助社区包:

npm install xlsx
# 或者
yarn add xlsx

第三步:实现 Excel 读取工具

创建 utils/excelReader.js

const XLSX = require('xlsx');
const path = require('path');
class ExcelReader {
/**
   * 读取Excel文件
   * @param {string} filePath - Excel文件路径
   * @param {string} sheetName - 工作表名称(可选,默认为第一个)
   * @returns {Array} 测试数据数组
   */
static readTestData(filePath, sheetName = null) {
    try {
      // 解析文件路径
      const absolutePath = path.resolve(__dirname, '..', filePath);
      
      // 读取工作簿
      const workbook = XLSX.readFile(absolutePath);
      
      // 获取工作表
      const sheet = sheetName 
        ? workbook.Sheets[sheetName]
        : workbook.Sheets[workbook.SheetNames[0]];
      
      if (!sheet) {
        thrownewError(`工作表 ${sheetName || '第一个'} 不存在`);
      }
      
      // 转换为JSON
      const jsonData = XLSX.utils.sheet_to_json(sheet);
      
      console.log(`成功从 ${filePath} 读取 ${jsonData.length} 条测试数据`);
      return jsonData;
      
    } catch (error) {
      console.error('读取Excel文件失败:', error.message);
      throw error;
    }
  }
/**
   * 按测试场景筛选数据
   * @param {string} filePath - Excel文件路径
   * @param {string} scenario - 测试场景名称
   */
static getDataByScenario(filePath, scenario) {
    const allData = this.readTestData(filePath);
    return allData.filter(row => row['测试场景'] === scenario);
  }
}
module.exports = ExcelReader;

第四步:在测试中使用

现在,让我们重写登录测试:

const { test, expect } = require('@playwright/test');
const ExcelReader = require('../utils/excelReader');
test.describe('登录功能数据驱动测试', () => {
let testData;
  test.beforeAll(() => {
    // 一次性读取所有测试数据
    testData = ExcelReader.readTestData('./data/testdata.xlsx');
    console.log(`本次执行将运行 ${testData.length} 个测试用例`);
  });
  test('数据驱动登录测试', async ({ page }) => {
    // 遍历每条测试数据
    for (const data of testData) {
      // 使用测试场景作为子测试名称
      await test.step(`测试场景: ${data['测试场景']}`, async () => {
        console.log(`执行用例: ${data['测试场景']}, 用户名: ${data.username}`);
        
        // 导航到登录页
        await page.goto('https://your-app.com/login');
        
        // 使用数据填充表单
        await page.fill('#username', data.username);
        await page.fill('#password', data.password);
        await page.click('#login-btn');
        
        // 根据预期结果进行断言
        if (data.expected_result === '登录成功') {
          await expect(page).toHaveURL('https://your-app.com/dashboard');
          await expect(page.locator('.welcome-message')).toContainText(data.username);
        } elseif (data.expected_result.includes('提示')) {
          await expect(page.locator('.error-message')).toBeVisible();
          await expect(page.locator('.error-message')).toContainText(data.expected_result);
        }
        
        // 如果是管理员登录的特殊断言
        if (data.username === 'admin' && data.expected_result === '跳转管理后台') {
          await expect(page).toHaveURL('https://your-app.com/admin');
        }
      });
    }
  });
});

Excel 方案的优缺点

优点:

  • 非技术人员也能轻松维护
  • 支持复杂的数据格式(合并单元格、公式等)
  • 可以用 Excel 的数据验证功能保证数据质量

缺点:

  • 需要额外依赖
  • 版本控制时二进制文件对比困难
  • 读取速度相对较慢

实战二:从 JSON 读取测试数据

如果你团队里都是开发人员,或者你更喜欢纯文本的版本控制,JSON 可能是更好的选择。

第一步:创建 JSON 数据文件

创建 data/loginTestData.json

{
  "login_cases": [
    {
      "test_scenario": "普通用户登录",
      "username": "zhangsan",
      "password": "123456",
      "expected_result": "登录成功",
      "permissions": ["view", "edit"],
      "metadata": {
        "priority": "P0",
        "tags": ["smoke", "regression"]
      }
    },
    {
      "test_scenario": "管理员登录",
      "username": "admin",
      "password": "admin@123",
      "expected_result": "跳转管理后台",
      "permissions": ["view", "edit", "delete", "admin"],
      "metadata": {
        "priority": "P1",
        "tags": ["regression"]
      }
    },
    {
      "test_scenario": "密码错误",
      "username": "lisi",
      "password": "wrong_pwd",
      "expected_result": "提示密码错误",
      "metadata": {
        "priority": "P2",
        "tags": ["negative"]
      }
    }
  ],
"environment_config": {
    "base_url": "https://your-app.com",
    "timeout": 30000
  }
}

第二步:创建 JSON 读取工具

创建 utils/jsonReader.js

const fs = require('fs').promises;
const path = require('path');
class JsonReader {
/**
   * 读取JSON测试数据
   * @param {string} filePath - JSON文件路径
   * @returns {Promise<Object>} 解析后的JSON对象
   */
staticasync readTestData(filePath) {
    try {
      const absolutePath = path.resolve(__dirname, '..', filePath);
      const fileContent = await fs.readFile(absolutePath, 'utf-8');
      const jsonData = JSON.parse(fileContent);
      
      console.log(`从 ${filePath} 加载了 ${jsonData.login_cases?.length || 0} 个登录测试用例`);
      return jsonData;
      
    } catch (error) {
      if (error.code === 'ENOENT') {
        console.error(`文件不存在: ${filePath}`);
      } elseif (error instanceofSyntaxError) {
        console.error(`JSON格式错误: ${error.message}`);
      }
      throw error;
    }
  }
/**
   * 根据标签过滤测试用例
   * @param {string} filePath - JSON文件路径
   * @param {string} tag - 标签名称
   */
staticasync getCasesByTag(filePath, tag) {
    const data = awaitthis.readTestData(filePath);
    if (!data.login_cases) return [];
    
    return data.login_cases.filter(testCase =>
      testCase.metadata?.tags?.includes(tag)
    );
  }
/**
   * 获取环境配置
   * @param {string} filePath - JSON文件路径
   */
staticasync getConfig(filePath) {
    const data = awaitthis.readTestData(filePath);
    return data.environment_config || {};
  }
}
module.exports = JsonReader;

第三步:在测试中使用 JSON 数据

const { test, expect } = require('@playwright/test');
const JsonReader = require('../utils/jsonReader');
test.describe('JSON数据驱动登录测试', () => {
let testCases;
let config;
  test.beforeAll(async () => {
    // 异步读取数据和配置
    const testData = await JsonReader.readTestData('./data/loginTestData.json');
    testCases = testData.login_cases;
    config = testData.environment_config;
    
    console.log(`基础URL: ${config.base_url}, 超时: ${config.timeout}ms`);
  });
// 只运行冒烟测试用例
  test('冒烟测试:登录功能', async ({ page }) => {
    const smokeCases = await JsonReader.getCasesByTag('./data/loginTestData.json', 'smoke');
    
    for (const testCase of smokeCases) {
      await test.step(`冒烟测试 - ${testCase.test_scenario}`, async () => {
        await page.goto(`${config.base_url}/login`);
        
        await page.fill('#username', testCase.username);
        await page.fill('#password', testCase.password);
        await page.click('#login-btn');
        
        // 使用环境配置中的超时时间
        await page.waitForTimeout(config.timeout);
        
        // 这里可以根据你的实际需求添加断言
        await expect(page).not.toHaveURL(`${config.base_url}/login`);
      });
    }
  });
// 运行所有测试用例,带详细断言
  test('完整登录测试套件', async ({ page }) => {
    for (const testCase of testCases) {
      await test.step(testCase.test_scenario, async () => {
        // 这里可以添加更复杂的测试逻辑
        console.log(`测试用户权限: ${testCase.permissions?.join(', ') || '无'}`);
        
        // 实际测试步骤...
        await page.goto(`${config.base_url}/login`);
        // ... 更多测试代码
      });
    }
  });
});

更高级的用法:配合 Playwright Fixtures

如果你想让测试数据在整个项目范围内可用,可以创建自定义 fixture:

// fixtures/testDataFixture.js
const { test: baseTest } = require('@playwright/test');
const JsonReader = require('../utils/jsonReader');
const test = baseTest.extend({
testData: async ({}, use) => {
    // 这里可以读取任何你需要的数据文件
    const data = await JsonReader.readTestData('./data/loginTestData.json');
    await use(data);
  },
smokeCases: async ({}, use) => {
    const cases = await JsonReader.getCasesByTag('./data/loginTestData.json', 'smoke');
    await use(cases);
  }
});
module.exports = { test };

然后在测试中直接使用:

const { test } = require('../fixtures/testDataFixture');
test('使用fixture的测试', async ({ page, testData, smokeCases }) => {
  console.log(`总用例数: ${testData.login_cases.length}`);
  console.log(`冒烟用例数: ${smokeCases.length}`);
  
  // ... 测试逻辑
});

JSON 方案的优缺点

优点:

  • 纯文本,版本控制友好
  • 无需额外依赖(Node.js 原生支持)
  • 结构灵活,支持嵌套数据
  • 读取速度快

缺点:

  • 非技术人员编辑困难
  • 没有 Excel 的数据验证功能
  • 容易因格式错误导致解析失败

如何选择?

根据我的经验,选择建议如下:

  1. 选 Excel 如果
  • 测试数据经常由产品/业务人员提供
  • 数据需要复杂的计算或格式
  • 已经有现成的 Excel 数据源
  1. 选 JSON 如果
  • 团队都是技术人员
  • 需要频繁进行版本控制和代码审查
  • 数据需要嵌套结构或复杂数据类型
  1. 混合使用(进阶方案):
// 用Excel作为数据源,但转换为JSON格式存储
const excelData = ExcelReader.readTestData('./data/raw/source.xlsx');
const jsonData = JSON.stringify(excelData, null, 2);
await fs.writeFile('./data/processed/testData.json', jsonData);
// 然后在测试中使用JSON版本

避坑指南

  1. 路径问题:始终使用 path.resolve 处理文件路径,避免不同操作系统下的问题。
  2. 数据验证:在读取数据后,添加验证逻辑:
function validateTestData(data) {
  const requiredFields = ['username', 'password', 'expected_result'];
  data.forEach((row, index) => {
    requiredFields.forEach(field => {
      if (!row[field]) {
        throw new Error(`第${index + 1}行缺少必要字段: ${field}`);
      }
    });
  });
}
  1. 性能优化:对于大量测试数据,考虑分批执行:
// 分批执行,每批5个用例
const batchSize = 5;
for (let i = 0; i < testData.length; i += batchSize) {
  const batch = testData.slice(i, i + batchSize);
  // 执行批次...
}
  1. 错误处理:添加详细的错误日志,方便调试:
try {
  await performTest(data);
} catch (error) {
  console.error(`用例失败: ${data.test_scenario}`, {
    username: data.username,
    error: error.message
  });
  // 可以继续执行下一个用例,而不是整个测试失败
}

总结

数据驱动测试不是银弹,但它是提升测试代码可维护性的重要手段。通过将测试数据从代码中分离出来:

  1. 你的测试逻辑会更简洁,不再充斥着各种硬编码值
  2. 测试数据维护成本大大降低,非技术人员也能参与
  3. 更容易实现测试用例的复用和组合
  4. 测试报告更清晰,每个数据行都可以作为一个独立的测试步骤

无论是选择 Excel 还是 JSON,关键是开始实践。从最简单的登录测试开始,逐步将你的测试套件改造为数据驱动模式。你会发现,当产品经理直接给你一个 Excel 文件说“把这些测试用例都跑一下”时,你的内心会是多么的平静。

最后提醒一点:数据驱动测试虽然好,但不要过度设计。简单的、不会频繁变化的测试数据,直接写在代码里也许更合适。找到适合你项目的平衡点,这才是真正的工程智慧。

相关文章
|
8月前
|
自然语言处理 前端开发 JavaScript
Playwright系列课(2) | 元素定位四大法宝:CSS/文本/XPath/语义化定位实战指南
本文是Playwright系列第二课,详解元素定位四大核心技术:CSS选择器、文本定位、XPath和语义化定位,结合实战演示各方法应用场景。重点解析Playwright智能定位器(Locator)的独特优势——自动等待与重试机制,通过预检元素可操作性(可见/可点击)有效规避网络延迟导致的脚本失效,显著提升自动化测试稳定性。
|
18天前
|
人工智能 缓存 自然语言处理
告别Demo|手把手教你构建可用的LangChain测试智能体
市面上从不缺少能跑通 Demo 的 AI 测试脚本,缺的是能在企业级复杂场景下真正“抗住事”的测试智能体。今天我们不谈概念,直接动手:基于 LangChain 从零构建一个具备测试设计、自主执行、结果分析能力的生产级 Agent。它将证明,AI 自动化测试的价值,不在于“看起来智能”,而在于能为你省下多少真实工时。
|
25天前
|
Web App开发 人工智能 前端开发
Playwright 面试必备:测试工程师的实战指南
太多新人把 Playwright 当“黑盒”用:脚本能跑就行,一问原理就懵。其实面试官不关心你用了多新潮的工具,而是你是否真正理解它、能否用它稳定解决问题。这篇整理自一线实战和大厂真题,专为测试/测开同学准备,帮你把“会用”变成“懂用”。
|
19天前
|
人工智能 自然语言处理 测试技术
Prompt Engineering 进阶:如何写出让 AI 自动生成高质量测试用例的提示词?
AI赋能测试用例设计,关键在结构化Prompt:需明确角色、业务、技术栈与约束,并融入等价类、状态图等测试方法论;要求表格化/代码化输出,辅以少样本示例和异常场景深挖。本质是将测试经验精准传递给AI。
|
2月前
|
测试技术 API 开发者
Playwright测试调试技巧:断点、日志与跟踪查看器的应用
本文将分享断点调试、智能日志与跟踪查看器这三个核心技巧,它们构成了解决复杂测试问题的强大工具箱,能帮助你像回放录像一样洞察每一次失败的根源。
|
3月前
|
数据采集 监控 NoSQL
基于n8n创建自愈式用例库及质量知识图谱
本文分享了如何基于n8n构建自愈型质量管理系统。通过自动化采集缺陷、需求等数据并构建知识图谱,系统能智能分析、自动修复用例库。方案大幅降低了维护耗时与缺陷逃逸率,将测试团队从重复劳动中解放,转向质量策略设计,实现了质量数据的持续流动与优化。
|
22天前
|
人工智能 JavaScript API
opencode 安装 -> 使用
OpenCode 是一款开源AI编程助手,支持智能代码生成与文件操作。需先安装Node.js(推荐v22),再通过scoop或npm全局安装。启动后可切换build/plan双模式,支持自定义API模型、多会话、对话导出与分享等功能。(239字)
1054 12
|
2月前
|
机器学习/深度学习 自然语言处理 前端开发
智谱大模型GLM-4.7火爆技术圈:探秘其高效实用的成长之路!
GLM-4.7的发布标志着大模型竞争进入工程化落地新阶段。其核心突破并非单纯参数增长,而是通过交织式思考等机制,显著提升了代码生成与多步任务执行(Agent)的稳定性和可交付性。智谱采用“先验证再上桌”的严谨数据筛选策略,并配套开源强化学习框架Slime,将模型训练打造成系统工程。这预示着未来竞争焦点将从“模型更聪明”转向“体系更可靠、更能干活”
|
3月前
|
前端开发 JavaScript 测试技术
告别Selenium时代:用Playwright解决Selenium的三大痛点
本文分享了团队从Selenium迁移至Playwright的完整历程。通过对比两者架构差异,Playwright因直连浏览器协议,显著提升了测试速度与稳定性。文章详述了分阶段迁移策略、实践技巧与避坑指南,最终实现回归时间缩短60%、维护成本下降70%。这次迁移不仅是工具升级,更推动了团队从“消防员”到“质量建筑师”的思维转变。
告别Selenium时代:用Playwright解决Selenium的三大痛点
|
3月前
|
人工智能 监控 前端开发
年终汇报新思路:领导真正关心的四个关键层面
年终汇报不是罗列工作量,而是论证自身价值。关键在于展示如何解决真问题、体现思考深度、与团队战略对齐,以及能为明年贡献什么。测试开发人员应聚焦于如何通过技术手段化解风险、提升效率,并将一次性解决方案沉淀为团队能力。一份精炼、目标明确的汇报,远比冗长的任务清单更有力量。

热门文章

最新文章