在当今快速迭代的软件开发环境中,自动化测试已成为保证产品质量的关键环节。然而,传统的测试脚本编写往往需要测试人员具备专业的编程技能,这在一定程度上限制了自动化测试的普及和应用效率。正是基于这样的背景,自然语言驱动测试应运而生。

本文将带你使用Playwright结合MCP(Model Context Protocol)框架,从零开始构建你的第一个自然语言驱动自动化测试。无论你是测试新手还是经验丰富的开发者,都能通过本指南快速掌握这一前沿技术。

环境准备

安装必要的工具

首先,确保你的系统已安装以下软件:

<span data-cacheurl="" data-remoteid="" data-lazy-bgimg="https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELsAmVaZ80rx3Wd4mibvPh7RibFlLLwHmicN4Y150RPPPLkPBn6pibS4SLMjgujibSnBnpeF6ccDX91WiaTUcuwknXHp75/640?wx_fmt=svg&amp;from=appmsg" data-fail="0"></span><code><span><span leaf=""># 安装Node.js(版本16或以上)</span></span><span leaf=""><br></span><span><span leaf=""># 从官网 https://nodejs.org/ 下载安装</span></span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf=""># 安装Playwright</span></span><span leaf=""><br></span><span leaf="">npm init playwright@latest</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf=""># 安装MCP相关依赖</span></span><span leaf=""><br></span><span leaf="">npm install @modelcontextprotocol/sdk playwright-core</span><span leaf=""><br></span></code>

项目结构初始化

创建项目目录并初始化基本结构:

<span data-cacheurl="" data-remoteid="" data-lazy-bgimg="https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELsAmVaZ80rx3Wd4mibvPh7RibFlLLwHmicN4Y150RPPPLkPBn6pibS4SLMjgujibSnBnpeF6ccDX91WiaTUcuwknXHp75/640?wx_fmt=svg&amp;from=appmsg"></span><code><span leaf="">my-nl-test-project/</span><span leaf=""><br></span><span leaf="">├── src/</span><span leaf=""><br></span><span leaf="">│ &nbsp; ├── mcp-client.js</span><span leaf=""><br></span><span leaf="">│ &nbsp; ├──&nbsp;</span><span><span leaf="">test</span></span><span leaf="">-generator.js</span><span leaf=""><br></span><span leaf="">│ &nbsp; └── utils/</span><span leaf=""><br></span><span leaf="">├── tests/</span><span leaf=""><br></span><span leaf="">│ &nbsp; └── generated/</span><span leaf=""><br></span><span leaf="">├── package.json</span><span leaf=""><br></span><span leaf="">└── README.md</span><span leaf=""><br></span></code>

构建MCP客户端

基础客户端实现

<span data-cacheurl="" data-remoteid="" data-lazy-bgimg="https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELsAmVaZ80rx3Wd4mibvPh7RibFlLLwHmicN4Y150RPPPLkPBn6pibS4SLMjgujibSnBnpeF6ccDX91WiaTUcuwknXHp75/640?wx_fmt=svg&amp;from=appmsg"></span><code><span><span leaf="">// src/mcp-client.js</span></span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;{ Client } =&nbsp;</span><span><span leaf="">require</span></span><span leaf="">(</span><span><span leaf="">'@modelcontextprotocol/sdk'</span></span><span leaf="">);</span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;{ PlaywrightTestGenerator } =&nbsp;</span><span><span leaf="">require</span></span><span leaf="">(</span><span><span leaf="">'./test-generator'</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf=""><br></span><span><span><span leaf="">class</span></span><span leaf="">&nbsp;</span><span><span leaf="">MCPTestClient</span></span><span leaf="">&nbsp;</span></span><span leaf="">{</span><span leaf=""><br></span><span><span leaf="">constructor</span></span><span leaf="">() {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.client =&nbsp;</span><span><span leaf="">new</span></span><span leaf="">&nbsp;Client({</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">name</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'playwright-test-generator'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">version</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'1.0.0'</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; });</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.testGenerator =&nbsp;</span><span><span leaf="">new</span></span><span leaf="">&nbsp;PlaywrightTestGenerator();</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">async</span></span><span leaf="">&nbsp;connectToServer(serverUrl) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">try</span></span><span leaf="">&nbsp;{</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">await</span></span><span><span leaf="">this</span></span><span leaf="">.client.connect(serverUrl);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">console</span></span><span leaf="">.log(</span><span><span leaf="">'成功连接到MCP服务器'</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }&nbsp;</span><span><span leaf="">catch</span></span><span leaf="">&nbsp;(error) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">console</span></span><span leaf="">.error(</span><span><span leaf="">'连接MCP服务器失败:'</span></span><span leaf="">, error);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">throw</span></span><span leaf="">&nbsp;error;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">async</span></span><span leaf="">&nbsp;generateTestFromNaturalLanguage(nlDescription) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;prompt =&nbsp;</span><span><span leaf="">`</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; 请将以下自然语言测试描述转换为Playwright测试代码:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; "</span><span><span leaf="">${nlDescription}</span></span><span leaf="">"</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; 要求:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; 1. 使用Page Object模式</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; 2. 包含必要的等待和断言</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; 3. 代码结构清晰易读</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; 4. 包含错误处理</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; 只返回JavaScript代码,不需要解释。</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; `</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;response =&nbsp;</span><span><span leaf="">await</span></span><span><span leaf="">this</span></span><span leaf="">.client.request({</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">method</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'tools/call'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">params</span></span><span leaf="">: {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">name</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'generate_code'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">arguments</span></span><span leaf="">: {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">prompt</span></span><span leaf="">: prompt,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">language</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'javascript'</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; });</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span leaf="">&nbsp;response.result.content;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">module</span></span><span leaf="">.exports = { MCPTestClient };</span><span leaf=""><br></span></code>

创建测试生成器

<span data-cacheurl="" data-remoteid="" data-lazy-bgimg="https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELsAmVaZ80rx3Wd4mibvPh7RibFlLLwHmicN4Y150RPPPLkPBn6pibS4SLMjgujibSnBnpeF6ccDX91WiaTUcuwknXHp75/640?wx_fmt=svg&amp;from=appmsg" data-fail="0"></span><code><span><span leaf="">// src/test-generator.js</span></span><span leaf=""><br></span><span><span><span leaf="">class</span></span><span leaf="">&nbsp;</span><span><span leaf="">PlaywrightTestGenerator</span></span><span leaf="">&nbsp;</span></span><span leaf="">{</span><span leaf=""><br></span><span leaf="">&nbsp; generateBaseTestStructure(testName) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">`</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; const { test, expect } = require('@playwright/test');</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; test('</span><span><span leaf="">${testName}</span></span><span leaf="">', async ({ page }) =&gt; {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; try {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 测试步骤将在这里生成</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; `</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; addNavigationStep(url) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">`</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 导航到页面</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await page.goto('</span><span><span leaf="">${url}</span></span><span leaf="">');</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await page.waitForLoadState('networkidle');</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; `</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; addClickStep(selector, description) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">`</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp;</span><span><span leaf="">${description}</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await page.click('</span><span><span leaf="">${selector}</span></span><span leaf="">');</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await page.waitForTimeout(1000);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; `</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; addFillStep(selector, value, description) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">`</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp;</span><span><span leaf="">${description}</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await page.fill('</span><span><span leaf="">${selector}</span></span><span leaf="">', '</span><span><span leaf="">${value}</span></span><span leaf="">');</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; `</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; addAssertionStep(assertionType, selector, expectedValue) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">switch</span></span><span leaf="">(assertionType) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">case</span></span><span><span leaf="">'visible'</span></span><span leaf="">:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">`</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 验证元素可见</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await expect(page.locator('</span><span><span leaf="">${selector}</span></span><span leaf="">')).toBeVisible();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; `</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">case</span></span><span><span leaf="">'text'</span></span><span leaf="">:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">`</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 验证文本内容</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await expect(page.locator('</span><span><span leaf="">${selector}</span></span><span leaf="">')).toHaveText('</span><span><span leaf="">${expectedValue}</span></span><span leaf="">');</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; `</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">case</span></span><span><span leaf="">'url'</span></span><span leaf="">:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">`</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 验证当前URL</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await expect(page).toHaveURL('</span><span><span leaf="">${expectedValue}</span></span><span leaf="">');</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; `</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">default</span></span><span leaf="">:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">''</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; completeTestStructure() {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">`</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; } catch (error) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.error('测试执行失败:', error);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw error;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; });</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; `</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">// 自然语言解析方法</span></span><span leaf=""><br></span><span leaf="">&nbsp; parseNaturalLanguage(description) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;steps = [];</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">if</span></span><span leaf="">&nbsp;(description.includes(</span><span><span leaf="">'登录'</span></span><span leaf="">)) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; steps.push({</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">type</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'navigation'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">url</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'https://example.com/login'</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; });</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">if</span></span><span leaf="">&nbsp;(description.includes(</span><span><span leaf="">'用户名'</span></span><span leaf="">)) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; steps.push({</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">type</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'fill'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">selector</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'#username'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">value</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'testuser'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">description</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'输入用户名'</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; });</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">if</span></span><span leaf="">&nbsp;(description.includes(</span><span><span leaf="">'密码'</span></span><span leaf="">)) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; steps.push({</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">type</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'fill'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">selector</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'#password'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">value</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'password123'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">description</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'输入密码'</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; });</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; steps.push({</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">type</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'click'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">selector</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'#login-btn'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">description</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'点击登录按钮'</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; });</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">if</span></span><span leaf="">&nbsp;(description.includes(</span><span><span leaf="">'成功'</span></span><span leaf="">)) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; steps.push({</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">type</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'assertion'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">assertionType</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'url'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">expectedValue</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'https://example.com/dashboard'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">description</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'验证登录成功'</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; });</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span leaf="">&nbsp;steps;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; generateTestFromSteps(testName, steps) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">let</span></span><span leaf="">&nbsp;testCode =&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.generateBaseTestStructure(testName);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; steps.forEach(</span><span><span><span leaf="">step</span></span><span leaf="">&nbsp;=&gt;</span></span><span leaf="">&nbsp;{</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">switch</span></span><span leaf="">(step.type) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">case</span></span><span><span leaf="">'navigation'</span></span><span leaf="">:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; testCode +=&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.addNavigationStep(step.url);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">break</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">case</span></span><span><span leaf="">'click'</span></span><span leaf="">:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; testCode +=&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.addClickStep(step.selector, step.description);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">break</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">case</span></span><span><span leaf="">'fill'</span></span><span leaf="">:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; testCode +=&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.addFillStep(step.selector, step.value, step.description);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">break</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">case</span></span><span><span leaf="">'assertion'</span></span><span leaf="">:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; testCode +=&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.addAssertionStep(</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; step.assertionType,&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; step.selector,&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; step.expectedValue</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">break</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; });</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; testCode +=&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.completeTestStructure();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span leaf="">&nbsp;testCode;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">module</span></span><span leaf="">.exports = { PlaywrightTestGenerator };</span><span leaf=""><br></span></code>

实现自然语言驱动测试

主程序入口

<span data-cacheurl="" data-remoteid="" data-lazy-bgimg="https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELsAmVaZ80rx3Wd4mibvPh7RibFlLLwHmicN4Y150RPPPLkPBn6pibS4SLMjgujibSnBnpeF6ccDX91WiaTUcuwknXHp75/640?wx_fmt=svg&amp;from=appmsg"></span><code><span><span leaf="">// src/main.js</span></span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;{ MCPTestClient } =&nbsp;</span><span><span leaf="">require</span></span><span leaf="">(</span><span><span leaf="">'./mcp-client'</span></span><span leaf="">);</span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;fs =&nbsp;</span><span><span leaf="">require</span></span><span leaf="">(</span><span><span leaf="">'fs'</span></span><span leaf="">);</span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;path =&nbsp;</span><span><span leaf="">require</span></span><span leaf="">(</span><span><span leaf="">'path'</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf=""><br></span><span><span><span leaf="">class</span></span><span leaf="">&nbsp;</span><span><span leaf="">NaturalLanguageTestRunner</span></span><span leaf="">&nbsp;</span></span><span leaf="">{</span><span leaf=""><br></span><span><span leaf="">constructor</span></span><span leaf="">() {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.mcpClient =&nbsp;</span><span><span leaf="">new</span></span><span leaf="">&nbsp;MCPTestClient();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.testCount =&nbsp;</span><span><span leaf="">0</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">async</span></span><span leaf="">&nbsp;initialize() {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">// 在实际应用中,这里应该连接到你选择的MCP服务器</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">// await this.mcpClient.connectToServer('your-mcp-server-url');</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">console</span></span><span leaf="">.log(</span><span><span leaf="">'自然语言测试运行器初始化完成'</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">async</span></span><span leaf="">&nbsp;createTestFromNaturalLanguage(description, testName =&nbsp;</span><span><span leaf="">null</span></span><span leaf="">) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;name = testName ||&nbsp;</span><span><span leaf="">`generated-test-</span><span><span leaf="">${++</span><span><span leaf="">this</span></span><span leaf="">.testCount}</span></span><span leaf="">`</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">console</span></span><span leaf="">.log(</span><span><span leaf="">`正在生成测试:&nbsp;</span><span><span leaf="">${name}</span></span><span leaf="">`</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">console</span></span><span leaf="">.log(</span><span><span leaf="">`测试描述:&nbsp;</span><span><span leaf="">${description}</span></span><span leaf="">`</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">// 方法1: 使用MCP服务器生成测试(推荐)</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">// const testCode = await this.mcpClient.generateTestFromNaturalLanguage(description);</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">// 方法2: 使用本地解析器生成测试(备选方案)</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;steps =&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.mcpClient.testGenerator.parseNaturalLanguage(description);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;testCode =&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.mcpClient.testGenerator.generateTestFromSteps(name, steps);</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">await</span></span><span><span leaf="">this</span></span><span leaf="">.saveTestFile(name, testCode);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">console</span></span><span leaf="">.log(</span><span><span leaf="">`测试文件已生成: tests/generated/</span><span><span leaf="">${name}</span></span><span leaf="">.spec.js`</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span leaf="">&nbsp;testCode;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">async</span></span><span leaf="">&nbsp;saveTestFile(testName, testCode) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;testDir = path.join(__dirname,&nbsp;</span><span><span leaf="">'..'</span></span><span leaf="">,&nbsp;</span><span><span leaf="">'tests'</span></span><span leaf="">,&nbsp;</span><span><span leaf="">'generated'</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">if</span></span><span leaf="">&nbsp;(!fs.existsSync(testDir)) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; fs.mkdirSync(testDir, {&nbsp;</span><span><span leaf="">recursive</span></span><span leaf="">:&nbsp;</span><span><span leaf="">true</span></span><span leaf="">&nbsp;});</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;filePath = path.join(testDir,&nbsp;</span><span><span leaf="">`</span><span><span leaf="">${testName}</span></span><span leaf="">.spec.js`</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; fs.writeFileSync(filePath, testCode,&nbsp;</span><span><span leaf="">'utf8'</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">async</span></span><span leaf="">&nbsp;runGeneratedTests() {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;{ exec } =&nbsp;</span><span><span leaf="">require</span></span><span leaf="">(</span><span><span leaf="">'child_process'</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;playwrightConfigPath = path.join(__dirname,&nbsp;</span><span><span leaf="">'..'</span></span><span leaf="">,&nbsp;</span><span><span leaf="">'playwright.config.js'</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">new</span></span><span><span leaf="">Promise</span></span><span leaf="">(</span><span><span leaf="">(</span><span><span leaf="">resolve, reject</span></span><span leaf="">) =&gt;</span></span><span leaf="">&nbsp;{</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; exec(</span><span><span leaf="">`npx playwright test tests/generated/ --config=</span><span><span leaf="">${playwrightConfigPath}</span></span><span leaf="">`</span></span><span leaf="">,&nbsp;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; (error, stdout, stderr) =&gt; {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">if</span></span><span leaf="">&nbsp;(error) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">console</span></span><span leaf="">.error(</span><span><span leaf="">'测试执行错误:'</span></span><span leaf="">, error);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reject(error);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;</span><span><span leaf="">else</span></span><span leaf="">&nbsp;{</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">console</span></span><span leaf="">.log(</span><span><span leaf="">'测试执行完成'</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">console</span></span><span leaf="">.log(stdout);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; resolve(stdout);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; );</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; });</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">// 使用示例</span></span><span leaf=""><br></span><span><span leaf="">async</span></span><span><span><span leaf="">function</span></span><span leaf="">&nbsp;</span><span><span leaf="">main</span></span><span leaf="">(</span><span></span><span leaf="">)&nbsp;</span></span><span leaf="">{</span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;runner =&nbsp;</span><span><span leaf="">new</span></span><span leaf="">&nbsp;NaturalLanguageTestRunner();</span><span leaf=""><br></span><span><span leaf="">await</span></span><span leaf="">&nbsp;runner.initialize();</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">// 示例1: 登录功能测试</span></span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;loginTest =&nbsp;</span><span><span leaf="">await</span></span><span leaf="">&nbsp;runner.createTestFromNaturalLanguage(</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">'测试用户登录功能:输入用户名和密码,点击登录按钮,验证登录成功跳转到仪表板页面'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">'user-login-test'</span></span><span leaf=""><br></span><span leaf="">&nbsp; );</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">// 示例2: 搜索功能测试</span></span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;searchTest =&nbsp;</span><span><span leaf="">await</span></span><span leaf="">&nbsp;runner.createTestFromNaturalLanguage(</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">'在首页搜索框中输入"Playwright教程",点击搜索按钮,验证搜索结果页面显示相关内容'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">'search-functionality-test'</span></span><span leaf=""><br></span><span leaf="">&nbsp; );</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">console</span></span><span leaf="">.log(</span><span><span leaf="">'生成的登录测试代码:'</span></span><span leaf="">);</span><span leaf=""><br></span><span><span leaf="">console</span></span><span leaf="">.log(loginTest);</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">// 运行生成的测试</span></span><span leaf=""><br></span><span><span leaf="">// await runner.runGeneratedTests();</span></span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">// 如果直接运行此文件,执行示例</span></span><span leaf=""><br></span><span><span leaf="">if</span></span><span leaf="">&nbsp;(</span><span><span leaf="">require</span></span><span leaf="">.main ===&nbsp;</span><span><span leaf="">module</span></span><span leaf="">) {</span><span leaf=""><br></span><span leaf="">&nbsp; main().catch(</span><span><span leaf="">console</span></span><span leaf="">.error);</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">module</span></span><span leaf="">.exports = { NaturalLanguageTestRunner };</span><span leaf=""><br></span></code>

高级功能扩展

测试数据管理

<span data-cacheurl="" data-remoteid="" data-lazy-bgimg="https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELsAmVaZ80rx3Wd4mibvPh7RibFlLLwHmicN4Y150RPPPLkPBn6pibS4SLMjgujibSnBnpeF6ccDX91WiaTUcuwknXHp75/640?wx_fmt=svg&amp;from=appmsg" data-fail="0"></span><code><span><span leaf="">// src/utils/test-data-manager.js</span></span><span leaf=""><br></span><span><span><span leaf="">class</span></span><span leaf="">&nbsp;</span><span><span leaf="">TestDataManager</span></span><span leaf="">&nbsp;</span></span><span leaf="">{</span><span leaf=""><br></span><span><span leaf="">constructor</span></span><span leaf="">() {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.testData =&nbsp;</span><span><span leaf="">new</span></span><span><span leaf="">Map</span></span><span leaf="">();</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; generateTestData(scenario) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;baseData = {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">user</span></span><span leaf="">: {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">username</span></span><span leaf="">:&nbsp;</span><span><span leaf="">`testuser_</span><span><span leaf="">${</span><span><span leaf="">Date</span></span><span leaf="">.now()}</span></span><span leaf="">`</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">email</span></span><span leaf="">:&nbsp;</span><span><span leaf="">`test_</span><span><span leaf="">${</span><span><span leaf="">Date</span></span><span leaf="">.now()}</span></span><span leaf="">@example.com`</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">password</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'TestPassword123!'</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; },</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">product</span></span><span leaf="">: {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">name</span></span><span leaf="">:&nbsp;</span><span><span leaf="">`测试产品_</span><span><span leaf="">${</span><span><span leaf="">Date</span></span><span leaf="">.now()}</span></span><span leaf="">`</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">price</span></span><span leaf="">:&nbsp;</span><span><span leaf="">Math</span></span><span leaf="">.floor(</span><span><span leaf="">Math</span></span><span leaf="">.random() *&nbsp;</span><span><span leaf="">1000</span></span><span leaf="">) +&nbsp;</span><span><span leaf="">1</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">description</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'自动化测试创建的产品'</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; };</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.testData.set(scenario, baseData);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span leaf="">&nbsp;baseData;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; getTestData(scenario) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span><span leaf="">this</span></span><span leaf="">.testData.get(scenario) ||&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.generateTestData(scenario);</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">module</span></span><span leaf="">.exports = { TestDataManager };</span><span leaf=""><br></span></code>

智能元素定位器

<span data-cacheurl="" data-remoteid="" data-lazy-bgimg="https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELsAmVaZ80rx3Wd4mibvPh7RibFlLLwHmicN4Y150RPPPLkPBn6pibS4SLMjgujibSnBnpeF6ccDX91WiaTUcuwknXHp75/640?wx_fmt=svg&amp;from=appmsg" data-fail="0"></span><code><span><span leaf="">// src/utils/smart-locator.js</span></span><span leaf=""><br></span><span><span><span leaf="">class</span></span><span leaf="">&nbsp;</span><span><span leaf="">SmartLocator</span></span><span leaf="">&nbsp;</span></span><span leaf="">{</span><span leaf=""><br></span><span><span leaf="">constructor</span></span><span leaf="">(page) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.page = page;</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">async</span></span><span leaf="">&nbsp;findElement(description) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">// 基于描述智能查找元素</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;strategies = [</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">// 按文本内容查找</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">`text=</span><span><span leaf="">${description}</span></span><span leaf="">`</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">// 按placeholder查找</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">`[placeholder*="</span><span><span leaf="">${description}</span></span><span leaf="">"]`</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">// 按label查找</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">`label=</span><span><span leaf="">${description}</span></span><span leaf="">`</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">// 按按钮文本查找</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">`button:has-text("</span><span><span leaf="">${description}</span></span><span leaf="">")`</span></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; ];</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">for</span></span><span leaf="">&nbsp;(</span><span><span leaf="">const</span></span><span leaf="">&nbsp;selector&nbsp;</span><span><span leaf="">of</span></span><span leaf="">&nbsp;strategies) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">const</span></span><span leaf="">&nbsp;element =&nbsp;</span><span><span leaf="">this</span></span><span leaf="">.page.locator(selector);</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">if</span></span><span leaf="">&nbsp;(</span><span><span leaf="">await</span></span><span leaf="">&nbsp;element.count() &gt;&nbsp;</span><span><span leaf="">0</span></span><span leaf="">) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">return</span></span><span leaf="">&nbsp;element.first();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">throw</span></span><span><span leaf="">new</span></span><span><span leaf="">Error</span></span><span leaf="">(</span><span><span leaf="">`未找到描述为"</span><span><span leaf="">${description}</span></span><span leaf="">"的元素`</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf="">&nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">module</span></span><span leaf="">.exports = { SmartLocator };</span><span leaf=""><br></span></code>

配置Playwright

<span data-cacheurl="" data-remoteid="" data-lazy-bgimg="https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELsAmVaZ80rx3Wd4mibvPh7RibFlLLwHmicN4Y150RPPPLkPBn6pibS4SLMjgujibSnBnpeF6ccDX91WiaTUcuwknXHp75/640?wx_fmt=svg&amp;from=appmsg" data-fail="0"></span><code><span><span leaf="">// playwright.config.js</span></span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;{ defineConfig, devices } =&nbsp;</span><span><span leaf="">require</span></span><span leaf="">(</span><span><span leaf="">'@playwright/test'</span></span><span leaf="">);</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">module</span></span><span leaf="">.exports = defineConfig({</span><span leaf=""><br></span><span><span leaf="">testDir</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'./tests'</span></span><span leaf="">,</span><span leaf=""><br></span><span><span leaf="">fullyParallel</span></span><span leaf="">:&nbsp;</span><span><span leaf="">true</span></span><span leaf="">,</span><span leaf=""><br></span><span><span leaf="">forbidOnly</span></span><span leaf="">: !!process.env.CI,</span><span leaf=""><br></span><span><span leaf="">retries</span></span><span leaf="">: process.env.CI ?&nbsp;</span><span><span leaf="">2</span></span><span leaf="">&nbsp;:&nbsp;</span><span><span leaf="">0</span></span><span leaf="">,</span><span leaf=""><br></span><span><span leaf="">workers</span></span><span leaf="">: process.env.CI ?&nbsp;</span><span><span leaf="">1</span></span><span leaf="">&nbsp;:&nbsp;</span><span><span leaf="">undefined</span></span><span leaf="">,</span><span leaf=""><br></span><span><span leaf="">reporter</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'html'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">use</span></span><span leaf="">: {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">baseURL</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'https://your-test-site.com'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">trace</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'on-first-retry'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">screenshot</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'only-on-failure'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; },</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">projects</span></span><span leaf="">: [</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">name</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'chromium'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">use</span></span><span leaf="">: { ...devices[</span><span><span leaf="">'Desktop Chrome'</span></span><span leaf="">] },</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; },</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">name</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'firefox'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">use</span></span><span leaf="">: { ...devices[</span><span><span leaf="">'Desktop Firefox'</span></span><span leaf="">] },</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; },</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">name</span></span><span leaf="">:&nbsp;</span><span><span leaf="">'webkit'</span></span><span leaf="">,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp;&nbsp;</span><span><span leaf="">use</span></span><span leaf="">: { ...devices[</span><span><span leaf="">'Desktop Safari'</span></span><span leaf="">] },</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; },</span><span leaf=""><br></span><span leaf="">&nbsp; ],</span><span leaf=""><br></span><span leaf="">});</span><span leaf=""><br></span></code>

实际应用示例

完整的测试场景

<span data-cacheurl="" data-remoteid="" data-lazy-bgimg="https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELsAmVaZ80rx3Wd4mibvPh7RibFlLLwHmicN4Y150RPPPLkPBn6pibS4SLMjgujibSnBnpeF6ccDX91WiaTUcuwknXHp75/640?wx_fmt=svg&amp;from=appmsg" data-fail="0"></span><code><span><span leaf="">// 示例:完整的电商流程测试</span></span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;e2eTestDescription =&nbsp;</span><span><span leaf="">`</span><span leaf=""><br></span><span leaf="">测试完整的电商购买流程:</span><span leaf=""><br></span><span leaf="">1. 用户打开电商网站首页</span><span leaf=""><br></span><span leaf="">2. 搜索"笔记本电脑"</span><span leaf=""><br></span><span leaf="">3. 从搜索结果中选择第一个商品</span><span leaf=""><br></span><span leaf="">4. 将商品加入购物车</span><span leaf=""><br></span><span leaf="">5. 进入购物车页面</span><span leaf=""><br></span><span leaf="">6. 点击结算按钮</span><span leaf=""><br></span><span leaf="">7. 填写收货地址信息</span><span leaf=""><br></span><span leaf="">8. 选择支付方式</span><span leaf=""><br></span><span leaf="">9. 确认订单并完成购买</span><span leaf=""><br></span><span leaf="">10. 验证订单成功页面显示</span><span leaf=""><br></span><span leaf="">`</span></span><span leaf="">;</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">// 生成并执行测试</span></span><span leaf=""><br></span><span><span leaf="">async</span></span><span><span><span leaf="">function</span></span><span leaf="">&nbsp;</span><span><span leaf="">runE2ETest</span></span><span leaf="">(</span><span></span><span leaf="">)&nbsp;</span></span><span leaf="">{</span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;runner =&nbsp;</span><span><span leaf="">new</span></span><span leaf="">&nbsp;NaturalLanguageTestRunner();</span><span leaf=""><br></span><span><span leaf="">await</span></span><span leaf="">&nbsp;runner.initialize();</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">const</span></span><span leaf="">&nbsp;testCode =&nbsp;</span><span><span leaf="">await</span></span><span leaf="">&nbsp;runner.createTestFromNaturalLanguage(</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; e2eTestDescription,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp;&nbsp;</span><span><span leaf="">'ecommerce-purchase-flow'</span></span><span leaf=""><br></span><span leaf="">&nbsp; );</span><span leaf=""><br></span><span leaf=""><br></span><span><span leaf="">console</span></span><span leaf="">.log(</span><span><span leaf="">'生成的端到端测试代码:'</span></span><span leaf="">);</span><span leaf=""><br></span><span><span leaf="">console</span></span><span leaf="">.log(testCode);</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span></code>

最佳实践和注意事项

1. 自然语言描述规范

  • 使用清晰、简洁的语言描述测试步骤

  • 明确指定预期的验证点

  • 包含具体的测试数据和条件

2. 测试维护策略

  • 定期审查生成的测试代码

  • 建立测试用例版本管理

  • 设置测试代码质量检查

3. 错误处理和调试

  • 为生成的测试添加充分的错误处理

  • 实现详细的日志记录

  • 建立测试失败分析机制

    推荐学习

    Playwright web 爬虫与AI智能体课程,限时免费,机会难得。扫码报名,参与直播,希望您在这场公开课中收获满满,开启智能自动化测试的新篇章!

    图片

总结

通过本指南,你已经学会了如何使用Playwright和MCP框架构建自然语言驱动的自动化测试。这种创新的测试方法具有以下优势:

  1. 降低门槛:让非技术人员也能参与测试用例创建

  2. 提高效率:快速生成复杂的测试场景

  3. 易于维护:通过自然语言描述即可更新测试

  4. 扩展性强:可轻松集成到现有CI/CD流程

虽然自然语言驱动测试还处于发展阶段,但它代表了测试自动化的未来方向。随着AI技术的不断进步,我们有理由相信这种智能化的测试方式将在不久的将来成为行业标准。

开始尝试用自然语言描述你的测试需求,体验AI为你生成高质量测试代码的便利吧!