
1. 项目概述当自然语言遇见自动化测试最近在搞一个挺有意思的项目叫 Intv_AI_MK11。简单来说它的核心目标就是让测试工程师甚至是不太懂代码的产品经理能用大白话描述一个测试场景然后系统就能自动把这个描述变成可执行的自动化测试脚本。比如你对着系统说一句“帮我测试一下用户登录功能输入正确的用户名和密码点击登录按钮验证是否跳转到首页”它就能给你生成一段可以直接在 Selenium 或者 Appium 里跑的 Python 或 Java 脚本。这听起来是不是有点像“许愿式编程”其实背后是自然语言处理NLP和自动化测试框架的深度结合。在当前的软件研发流程里自动化测试脚本的编写和维护一直是个体力活兼技术活。测试同学需要熟悉编程语言、测试框架如 pytest, unittest、以及各种驱动工具如 Selenium WebDriver, Appium。哪怕只是一个简单的功能点变更也可能需要去修改一堆脚本维护成本不低。Intv_AI_MK11 就是想解决这个痛点。它试图在“人类自然语言需求”和“机器可执行指令”之间架起一座桥梁。我花了些时间深入研究并实践了这套思路发现它不仅仅是概念上的酷炫在实际的敏捷开发和持续集成CI/CD流水线中确实能显著提升测试用例设计和脚本生成的效率尤其适合那些业务逻辑频繁变动的中后台系统测试。接下来我就把自己在探索“基于自然语言描述的测试用例实现”过程中的核心思路、关键技术选型、实操步骤以及踩过的那些坑系统地梳理一遍。2. 核心思路与方案选型为什么是“描述”转“脚本”在决定采用“自然语言描述生成测试脚本”这个方案前我们团队也评估过其他路径。比如传统的录制回放工具像 Selenium IDE虽然上手快但生成的脚本脆弱、难以维护页面元素一变动就失效。又比如完全依赖测试工程师手写脚本质量高、灵活性好但对人员技能要求高产出速度是瓶颈。2.1 为什么选择 NLP 自动化测试框架的路线我们最终锚定 NLP 这条路主要基于以下几个考量降低自动化门槛这是最直接的驱动力。让业务专家、产品经理也能参与测试用例的设计与“编写”他们只需要关注业务逻辑What to test而不必纠结于编程语法和 API 调用How to test。这能极大释放测试开发工程师的生产力让他们聚焦在更复杂的测试框架设计、性能测试和专项测试上。提升用例设计与脚本生成的一致性在传统模式下测试用例文档通常是 Excel 或 TestLink和自动化脚本是割裂的。经常出现文档更新了脚本没跟上或者脚本改了文档忘了更新。用自然语言描述作为“单一可信源”描述本身既是需求文档又是脚本生成的依据从源头上保证了一致性。适应快速迭代在敏捷开发中需求变更是常态。当业务逻辑调整时测试同学只需要用自然语言修改或新增测试场景描述然后重新触发脚本生成即可无需深入代码层逐行修改响应速度更快。知识沉淀与复用将常见的测试模式如“登录”、“搜索”、“添加购物车”抽象成自然语言模板或意图形成团队的测试知识库。新成员可以快速复用这些模式生成标准化的测试脚本降低了学习成本和脚本质量的方差。2.2 技术栈选型构建 Intv_AI_MK11 的四大支柱要实现这个目标我们需要一个融合了 NLP、测试框架和流程编排的技术栈。经过多轮 POC概念验证我们确定了以下核心组件自然语言理解NLU引擎这是大脑。我们需要它能理解测试描述中的“意图”Intent和“关键实体”Entity。例如在描述“用账号admin和密码123456登录系统”中意图是“登录”实体是用户名admin和密码123456。我们对比了使用开源框架如 Rasa和调用大语言模型LLMAPI如 GPT、文心一言两种方案。最终考虑到对测试领域术语的精准理解和可控性我们选择了Rasa作为核心 NLU 引擎并针对测试领域进行了大量的语料训练。对于更复杂的、需要一定推理能力的描述我们辅助性地调用 LLM API 进行意图增强和实体抽取。自动化测试框架这是手脚。它负责执行生成的脚本。我们选择了pytest作为主测试运行器因为它插件丰富、断言清晰、报告美观。UI 自动化方面Selenium用于 Web 测试Appium用于移动端测试。API 测试则选用requests库。框架的选择保证了生成的脚本是行业标准、可维护的。脚本生成器模板引擎这是翻译官。它将 NLU 引擎解析出的结构化数据意图、实体、操作流填充到预定义的测试脚本模板中生成最终的.py或.java文件。我们使用了Jinja2模板引擎它的语法灵活能很好地处理条件判断、循环等逻辑对应测试场景中的“如果...那么...”if-else分支。元素定位与管理器这是眼睛。UI 自动化最头疼的就是元素定位。我们建立了一个中心化的“页面元素仓库”Page Object Repository使用 YAML 或 JSON 文件存储每个页面的元素定位信息如 ID、XPath、CSS Selector。当 NLU 解析出“点击登录按钮”时脚本生成器会从仓库中查找“登录页面”下的“登录按钮”对应的定位器并插入到脚本中。这实现了页面元素信息与业务逻辑的分离元素变更只需更新仓库所有相关脚本在下次生成时自动更新。这个技术栈组合在灵活性与可控性之间取得了较好的平衡。下面我们就深入每个核心环节看看具体怎么实现。3. 核心模块深度解析与实现一个完整的 Intv_AI_MK11 系统其工作流可以概括为自然语言输入 - NLU 解析 - 操作序列构建 - 脚本模板渲染 - 可执行脚本输出。我们拆开每个环节细说。3.1 NLU 引擎的训练与领域适配直接用通用的 NLP 模型来理解测试指令效果往往不好因为它不懂“断言”、“定位”、“参数化”这些测试领域的行话。所以领域适配是关键。第一步定义测试领域的意图和实体我们像设计产品功能一样设计 NLU 的“理解能力”。首先梳理出测试场景中的核心意图例如login: 登录操作search: 搜索操作add_to_cart: 添加商品到购物车assert_element_present: 断言元素存在assert_text_equal: 断言文本相等input_text: 输入文本每个意图下定义相关的实体。以login为例实体username: 用户名实体password: 密码实体button: 按钮如“登录按钮”第二步收集和标注训练数据这是最耗时但最重要的一步。我们编写了数百条覆盖核心业务流程的自然语言测试描述并对其进行标注。[用](username: test_user)[和密码](password: pass123)[点击登录](button: 登录按钮)标注工具可以使用 Rasa 自带的rasa init生成的标注工具或者使用更专业的如 Doccano。标注的质量直接决定了模型的理解准确率。第三步配置与训练 Rasa 模型Rasa 的配置文件config.yml需要精心调优。我们主要调整了以下组件pipeline: 我们使用了WhitespaceTokenizer中文需用JiebaTokenizerRegexFeaturizer来识别如邮箱、手机号等模式CRFEntityExtractor进行实体抽取DIETClassifier同时进行意图分类和实体识别。对于复杂描述还会加入ResponseSelector。policies: 主要使用TEDPolicy和MemoizationPolicy来管理对话流虽然我们当前是单轮指令但为后续支持多步骤测试场景预留了可能。训练命令很简单rasa train。但关键在于迭代根据模型在测试集上的表现不断补充和修正训练数据。实操心得不要指望一蹴而就。初期模型可能会把“验证登录成功”错误分类为assert_text而不是assert_element_present。这就需要我们把“验证...成功”这类表述同时加入到多个相关意图的训练数据中并确保实体标注清晰。一个技巧是为每个意图设计尽可能多样的口语化表达方式。3.2 从解析结果到操作序列测试步骤的抽象NLU 引擎输出的是一堆结构化的数据我们需要把它们转换成一个有序的“操作序列”Action Sequence。这是脚本生成前的关键中间表示。例如对于输入“首先访问首页然后搜索‘智能手机’检查结果列表是否包含‘小米’品牌最后将第一个结果加入购物车。”NLU 可能解析出多个意图。我们需要一个“流程解析器”来组织它们navigate(url: 首页)input_text(field: 搜索框 text: ‘智能手机’)click(button: 搜索按钮)assert_text_contains(element: 结果列表 text: ‘小米’)click(button: 第一个商品的‘加入购物车’按钮)这个序列必须符合 Selenium 等工具的实际执行逻辑比如点击前元素必须可见。我们设计了一个“操作字典”为每种意图定义了对应的基础操作单元、前置条件和后置动作。意图对应基础操作所需实体前置条件生成代码片段模板变量login输入文本、点击username, password, button页面已加载driver.find_element({% raw %}{{username_locator}}{% endraw %}).send_keys({% raw %}{{username}}{% endraw %}); ...assert_element_present等待与断言element-assert wait.until(EC.presence_of_element_located((By.XPATH, {% raw %}{{element_locator}}{% endraw %})))input_text输入文本field, text元素可交互driver.find_element({% raw %}{{field_locator}}{% endraw %}).send_keys({% raw %}{{text}}{% endraw %})这个“流程解析器”还负责处理一些隐含逻辑。比如描述中的“然后”、“最后”等词被解析为顺序执行“如果...就...”被解析为条件分支。这里我们会引入一个简单的规则引擎或者直接利用 Jinja2 模板的条件语句来实现分支逻辑的生成。3.3 脚本模板与 Jinja2 的动态渲染有了操作序列和从“元素仓库”查找到的定位器就可以渲染最终脚本了。我们为不同类型的测试UI、API准备了不同的基础模板。一个典型的 Web UI 测试脚本模板Jinja2 语法如下import pytest from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class TestGeneratedScript: classmethod def setup_class(cls): cls.driver webdriver.Chrome() cls.driver.implicitly_wait(10) cls.wait WebDriverWait(cls.driver, 10) classmethod def teardown_class(cls): cls.driver.quit() def test_{% raw %}{{ test_case_name }}{% endraw %} (self): {% raw %}{{ nl_description }}{% endraw %} # 以下是自动生成的操作步骤 {% for step in action_sequence -%} # Step {{ loop.index }}: {{ step.description }} {% if step.action_type navigate -%} self.driver.get({% raw %}{{ step.url }}{% endraw %}) {% elif step.action_type input_text -%} self.driver.find_element(By.{% raw %}{{ step.locator_type }}{% endraw %}, {% raw %}{{ step.locator_value }}{% endraw %}).send_keys({% raw %}{{ step.text }}{% endraw %}) {% elif step.action_type click -%} self.driver.find_element(By.{% raw %}{{ step.locator_type }}{% endraw %}, {% raw %}{{ step.locator_value }}{% endraw %}).click() {% elif step.action_type assert_element_present -%} element self.wait.until(EC.presence_of_element_located((By.{% raw %}{{ step.locator_type }}{% endraw %}, {% raw %}{{ step.locator_value }}{% endraw %}))) assert element is not None {% endif -%} {% endfor -%} if __name__ __main__: pytest.main([-v, __file__])渲染过程就是填充模板变量的过程。action_sequence这个列表变量包含了之前构建的所有步骤对象每个对象都有action_type、description、locator_type如XPATH,ID、locator_value等属性。Jinja2 的循环和条件语句能优雅地处理不同操作类型的代码生成。注意事项模板的设计要兼顾可读性和通用性。生成的脚本不应该是一坨难以阅读的代码。我们会在模板中加入合理的注释如# Step 1: ...并使用Page Object模式的高级模板当检测到操作序列频繁使用同一页面元素时生成更易于维护的 Page Object 类。3.4 元素仓库维护UI自动化的“地图”这是保障脚本长期可维护性的基石。我们用一个 YAML 文件来定义页面元素login_page: username_input: locator_type: ID locator_value: username description: 用户名输入框 password_input: locator_type: ID locator_value: password login_button: locator_type: XPATH locator_value: //button[contains(text(),登录)] home_page: search_box: locator_type: NAME locator_value: q当 NLU 解析出“在搜索框输入‘xxx’”时“流程解析器”会去元素仓库的home_page下查找search_box将其定位信息NAME, q注入到操作序列中最终成为脚本模板里的By.NAME, q。如何与开发协作维护这个仓库我们尝试过几种方式手动维护初期简单但随页面迭代易落后。从代码中提取与前端团队约定为可测试元素添加固定的>def test_user_login(self): 测试用户登录功能打开登录页输入用户名‘demo_user’和密码‘demo_pass’点击登录按钮验证页面跳转后顶部导航栏显示用户名‘demo_user’。”” # Step 1: 打开登录页 self.driver.get(https://www.example.com/login) # Step 2: 输入用户名‘demo_user’ self.driver.find_element(By.ID, username).send_keys(demo_user) # Step 3: 输入密码‘demo_pass’ self.driver.find_element(By.ID, password).send_keys(demo_pass) # Step 4: 点击登录按钮 self.driver.find_element(By.XPATH, //button[typesubmit]).click() # Step 5: 验证顶部导航栏显示用户名‘demo_user’ # 等待页面跳转完成通常可以等待新页面某个元素出现 self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, user-name))) user_name_element self.driver.find_element(By.CLASS_NAME, user-name) assert user_name_element.text demo_user第四步集成与执行生成的脚本可以直接放入项目的测试目录用pytest test_login.py命令运行。也可以集成到 CI/CD 流水线如 Jenkins、GitLab CI中在代码合并后自动触发回归测试。5. 避坑指南与效能提升在实际搭建和使用的过程中我们遇到了不少问题也总结出一些提升效能的技巧。5.1 常见问题与解决方案问题现象可能原因排查与解决思路NLU 解析意图错误训练数据不足或存在歧义描述过于口语化、模糊。1. 查看 Rasa 的nlu日志确认模型对当前句子的置信度。2. 将解析错误的句子加入训练集重新标注和训练。3. 规范描述用语建立团队内部的“测试描述规范”例如统一使用“点击XX按钮”、“在XX输入框输入YY”。生成的脚本运行时报“元素找不到”元素定位器过期前端代码已改页面加载过慢脚本执行太快。1.首要检查元素仓库中的定位器是否最新。建立元素变更通知机制。2. 在模板中为关键操作如点击、输入增加显式等待WebDriverWait而非仅仅使用隐式等待。3. 考虑使用更稳定的定位策略如优先使用ID或>脚本可读性和维护性差模板设计过于简单生成的是线性脚本缺乏必要的注释和结构。1. 升级模板引入Page Object 模式。当同一页面的操作达到一定频率时自动生成对应的 Page 类。2. 在模板中强制加入步骤描述作为注释。3. 对生成的脚本进行简单的代码格式化如使用black。复杂逻辑循环、条件分支无法描述自然语言描述难以清晰表达复杂控制流当前 NLU 和流程解析器不支持。1. 定义一套有限的、用于描述控制流的“关键词”如“对于列表中的每一项执行...”、“如果价格大于100则...”。在流程解析器中增加对这些关键词的规则处理。2. 对于极其复杂的场景暂时退回传统手动编码或将其拆分为多个简单的自然语言描述用例。处理动态内容或异步加载困难页面内容通过 AJAX 动态生成元素出现时机不确定。1. 在断言或后续操作前必须使用显式等待等待特定元素出现、可点击或包含特定文本。2. 在元素定位器中尽量避免使用包含动态索引如div[1]的绝对 XPATH使用相对路径或依赖其他稳定属性。5.2 提升生成脚本的健壮性引入重试机制在生成的脚本模板中对容易失败的操作如点击、网络请求包裹重试逻辑。可以使用tenacity库或自己实现简单的try-except重试。from tenacity import retry, stop_after_attempt, wait_fixed retry(stopstop_after_attempt(3), waitwait_fixed(2)) def click_login_button(): driver.find_element(...).click()自动生成数据清理代码对于创建了数据的测试如新建订单在模板的teardown方法或pytest fixture中自动插入数据清理的 API 调用保证测试环境的洁净。参数化测试数据当描述中出现“用一组用户名和密码测试登录”时NLU 应能识别出参数化意图。脚本生成器则使用pytest.mark.parametrize来生成参数化测试使得一个脚本能覆盖多组数据。5.3 与现有测试流程的融合Intv_AI_MK11 不是要取代所有手工测试脚本而是作为补充和提效工具。适用场景非常适合生成冒烟测试、核心业务流程回归测试的脚本。这些用例通常稳定、重要且描述清晰。分工建议测试分析师或产品经理负责用自然语言编写和维护核心用例库。测试开发工程师负责维护 NLU 模型、脚本模板、元素仓库以及处理复杂、特殊的测试场景。流程集成将脚本生成作为 CI/CD 流水线的一个环节。例如在代码评审Pull Request时自动根据修改的文件关联的测试用例描述生成或更新测试脚本并作为预提交检查的一部分运行。6. 总结与展望AI在测试自动化中的角色走完这一整套流程我的切身感受是基于自然语言生成测试脚本其最大价值不在于完全取代测试开发工程师而在于改变协作模式和提升效率基线。它把测试自动化的能力部分“民主化”了让更熟悉业务的人能直接参与自动化资产的创造。目前这套系统在处理清晰、线性的功能流程上已经相当可靠。但在面对高度动态交互、复杂验证逻辑如涉及多个系统状态比对或视觉验证时仍有局限。这些领域或许需要结合图像识别、更复杂的逻辑推理模型乃至强化学习来突破。另一个重要的体会是维护成本从“代码层”部分转移到了“描述层”和“模型层”。你需要持续维护那个元素仓库需要不断用新的表达方式去训练 NLU 模型以保持其理解力。这要求团队建立新的维护规范和习惯。最后工具再好也离不开人的智慧。清晰的测试思维、严谨的业务理解仍然是写出高质量测试描述无论是自然语言还是代码的前提。Intv_AI_MK11 这类工具更像是给测试人员配上了一副“翻译眼镜”让他们能更顺畅地将业务意图转化为机器可验证的动作。而如何定义“正确的业务意图”永远是人类测试专家不可替代的核心价值。