
概述前两篇我们搭好了环境也看到了AI生成Pytest代码的效果但AI是怎么知道要测什么的第一篇里我们直接把除法函数的源代码喂给AI让它生成测试用例。这种方式叫代码驱动。AI看实现来写测试。但在真实项目里你很少能只靠看代码就写出完整的测试。你需要读需求文档需要跟产品经理对齐预期需要知道业务规则是什么。这就需要一个前置步骤。把人类的自然语言需求转成结构化的测试策略。这一篇我们就来实现这个模块。从一段需求描述开始让AI自动生成包含测试场景、边界条件、预期结果的完整测试策略。意图理解模块是做什么的用一句话说输入自然语言输出结构化的测试策略。具体来看输入是一段需求描述比如产品经理发来的文档。输出是一个JSON格式的测试策略包含这些字段{feature:功能名称,test_scenarios:[{scenario_id:TC-001,name:场景名称,description:场景描述,preconditions:[前置条件1,前置条件2],steps:[步骤1,步骤2,步骤3],expected:预期结果,priority:high/medium/low,type:positive/negative/boundary}]}这个JSON我们会直接喂给用例生成模块也就是第一篇里那个生成Pytest代码的脚本所以意图理解模块的输出格式决定了后续所有环节的质量。注意意图理解模块只负责策略它不生成任何Python代码只输出结构化的测试设计这样做有三个好处职责分离意图理解管测什么用例生成管怎么测。可解释性测试策略是JSON格式人能直接读也能直接改。可复用同一个测试策略可以对接不同的代码生成逻辑。为什么需要意图理解模块我们先回顾一下第二篇的架构图AI Agent测试系统有四个模块意图理解 → 用例生成 → 执行收集 → 分析修复意图理解是第一环传统模式下这个环节是纯人工的产品经理写一份需求文档测试开发工程师阅读文档理解业务逻辑设计测试场景并把场景翻译成代码。这道工序有三个问题信息损耗需求文档写的和工程师理解的可能有偏差尤其当需求写得模糊的时候不同的人理解完全不同。效率瓶颈一个中等规模的需求从阅读到提炼出完整测试策略至少半天需求频繁变更的话这个工作要反复做。知识流失测试策略只存在于代码里代码只告诉你测什么不告诉你为什么这么测导致老的测试工程师走了新来的看不懂之前为什么设计这些用例。AI Agent在这里的价值不是取代你理解需求是帮你加速理解。如何实现意图理解模块理论讲完了我们用代码来实际的理解一下3.1 核心实现# intent_understanding.pyimportjsonimportosfromtypingimportList,Dict,OptionalfrompathlibimportPathfromdotenvimportload_dotenvfromopenaiimportOpenAIclassIntentUnderstandingAgent: 意图理解Agent。 输入需求描述输出结构化的测试策略JSON。 def__init__(self,model:strgpt-4o):load_dotenv()self.clientOpenAI(api_keyos.getenv(OPENAI_API_KEY),base_urlos.getenv(OPENAI_BASE_URL,None))self.modelmodeldefunderstand(self,requirement:str)-Dict:输入需求描述返回测试策略。promptself._build_prompt(requirement)try:responseself.client.chat.completions.create(modelself.model,messages[{role:system,content:self._get_system_prompt()},{role:user,content:prompt}],temperature0.2,response_format{type:json_object})resultjson.loads(response.choices[0].message.content)returnresultexceptExceptionase:print(f意图理解失败:{e})return{error:str(e)}def_get_system_prompt(self)-str:return 你是一位资深的测试架构师擅长从需求描述中提取测试策略。 你的任务是将用户提供的需求描述转化为结构化的测试策略JSON。 输出格式必须严格遵循以下结构 { feature: 功能名称, description: 功能描述, test_scenarios: [ { scenario_id: TC-001, name: 场景名称, description: 场景描述, preconditions: [前置条件1, 前置条件2], steps: [操作步骤1, 操作步骤2, 操作步骤3], expected: 预期结果, priority: high|medium|low, type: positive|negative|boundary } ], total_scenarios: 数字, coverage_notes: 覆盖情况说明 } 设计原则 1. 每个需求至少包含正常路径和异常路径 2. 边界条件是加分项 3. 每个场景的expected必须可验证 4. 优先级核心功能用high异常场景用medium边界场景用low 5. 场景之间不要重复覆盖 def_build_prompt(self,requirement:str)-str:returnf 请根据以下需求描述生成测试策略 ---{requirement}--- 请输出JSON格式的测试策略。 defsave_strategy(strategy:Dict,filepath:strtest_strategy.json):withopen(filepath,w,encodingutf-8)asf:json.dump(strategy,f,indent2,ensure_asciiFalse)print(f测试策略已保存:{filepath})if__name____main__:requirement 需求用户登录功能 用户可以使用手机号或邮箱进行登录。 登录时需要验证密码是否正确。 登录成功后的行为 - 跳转到个人主页 - 在顶部显示用户昵称 - 在右上角显示用户头像 登录失败的场景 - 手机号/邮箱不存在提示账号不存在 - 密码错误提示密码错误 - 账号被锁定提示账号已被锁定请联系客服 安全要求 - 连续输错5次密码后账号锁定30分钟 - 密码输入框不支持粘贴 性能要求 - 登录接口响应时间小于500msP95 agentIntentUnderstandingAgent()strategyagent.understand(requirement)iferrornotinstrategy:save_strategy(strategy,login_strategy.json)print(f\n测试策略摘要)print(f 功能:{strategy.get(feature,未知)})print(f 场景数:{strategy.get(total_scenarios,0)})forscinstrategy.get(test_scenarios,[]):print(f - [{sc[type]}]{sc[name]}(优先级:{sc[priority]}))else:print(f生成失败:{strategy[error]})3.2 输出示例运行上面的脚本生成的login_strategy.json{feature:用户登录,description:用户通过手机号或邮箱进行身份验证成功进入个人主页,test_scenarios:[{scenario_id:TC-001,name:使用手机号正确登录,description:用户使用正确的手机号和密码登录,preconditions:[用户已注册手机号13800001111密码正确],steps:[打开登录页面,输入手机号 13800001111,输入正确密码,点击登录按钮],expected:跳转到个人主页顶部显示用户昵称右上角显示用户头像,priority:high,type:positive},{scenario_id:TC-002,name:使用邮箱正确登录,description:用户使用正确的邮箱和密码登录,preconditions:[用户已注册邮箱 testexample.com密码正确],steps:[打开登录页面,输入邮箱 testexample.com,输入正确密码,点击登录按钮],expected:跳转到个人主页,priority:high,type:positive},{scenario_id:TC-003,name:登录失败-账号不存在,description:使用不存在的账号登录,preconditions:[],steps:[打开登录页面,输入手机号 13900001111未注册,输入任意密码,点击登录按钮],expected:提示账号不存在停留在登录页,priority:high,type:negative},{scenario_id:TC-004,name:登录失败-密码错误,description:密码输入错误,preconditions:[用户已注册手机号13800001111],steps:[打开登录页面,输入手机号 13800001111,输入错误密码,点击登录按钮],expected:提示密码错误停留在登录页,priority:high,type:negative},{scenario_id:TC-005,name:登录失败-账号被锁定,description:已锁定账号无法登录,preconditions:[账号已被管理员锁定],steps:[打开登录页面,输入锁定账号的手机号,输入正确密码,点击登录按钮],expected:提示账号已被锁定请联系客服停留在登录页,priority:medium,type:negative},{scenario_id:TC-006,name:连续输错密码触发锁定,description:连续5次密码错误后账号锁定30分钟,preconditions:[用户已注册手机号13800001111],steps:[打开登录页面,输入手机号 13800001111,输入错误密码,点击登录按钮,重复5次],expected:第5次错误后提示账号已被锁定30分钟30分钟内无法登录,priority:high,type:boundary},{scenario_id:TC-007,name:密码框禁止粘贴,description:密码输入框不允许粘贴操作,preconditions:[],steps:[打开登录页面,在密码框尝试粘贴],expected:粘贴无效密码框内容不变,priority:low,type:negative},{scenario_id:TC-008,name:登录接口响应时间,description:验证登录接口响应时间,preconditions:[系统正常运行],steps:[发送登录请求,记录响应时间],expected:响应时间小于500ms,priority:medium,type:positive}],total_scenarios:8,coverage_notes:覆盖手机号登录、邮箱登录、账号不存在、密码错误、账号锁定、连续错误锁定、禁止粘贴、响应时间包含2个正向、4个负向、2个边界场景}8个测试场景涵盖正向、负向、边界功能、安全、性能三个维度如果人工来做的话半天工作量。但是交给AI则10秒。3.3 加上输出校验AI的输出有时候不稳定。字段缺失、类型错误的情况偶尔会出现生产环境里不要直接把AI的输出喂给下游。先做校验。用Pydantic定义一个严格的输出模型# intent_understanding_with_validation.pyfrompydanticimportBaseModel,Field,validatorfromtypingimportList,OptionalimportjsonclassTestScenario(BaseModel):scenario_id:strField(...,description场景编号)name:strField(...,description场景名称)description:strField(...,description场景描述)preconditions:List[str]Field(default_factorylist)steps:List[str]Field(...,description操作步骤)expected:strField(...,description预期结果)priority:strField(...,descriptionhigh/medium/low)type:strField(...,descriptionpositive/negative/boundary)validator(priority)defvalidate_priority(cls,v):allowed[high,medium,low]ifv.lower()notinallowed:raiseValueError(fpriority必须是{allowed}之一)returnv.lower()validator(type)defvalidate_type(cls,v):allowed[positive,negative,boundary]ifv.lower()notinallowed:raiseValueError(ftype必须是{allowed}之一)returnv.lower()classTestStrategy(BaseModel):feature:strField(...,description功能名称)description:strField(...,description功能描述)test_scenarios:List[TestScenario]total_scenarios:intcoverage_notes:Optional[str]Nonevalidator(total_scenarios)defvalidate_total(cls,v,values):iftest_scenariosinvalues:actuallen(values[test_scenarios])ifv!actual:raiseValueError(ftotal_scenarios应该是{actual}实际是{v})returnvdefvalidate_strategy(raw:Dict)-Optional[TestStrategy]:try:returnTestStrategy(**raw)exceptExceptionase:print(f校验失败:{e})returnNone有了这个校验下游收到的数据一定是合法的。3.4 导出Markdown报告JSON是给机器用的。团队里其他人要看测试策略给一份可读性好的文档。defstrategy_to_markdown(strategy:Dict,output_file:strtest_strategy.md):lines[]lines.append(f# 测试策略{strategy.get(feature,未命名)}\n)lines.append(f{strategy.get(description,)}\n)lines.append(f**场景总数**{strategy.get(total_scenarios,0)}\n)type_count{positive:0,negative:0,boundary:0}forscinstrategy.get(test_scenarios,[]):tsc.get(type,)iftintype_count:type_count[t]1lines.append(**场景分布**)lines.append(f- 正向{type_count[positive]}个)lines.append(f- 负向{type_count[negative]}个)lines.append(f- 边界{type_count[boundary]}个\n)lines.append(---\n)forscinstrategy.get(test_scenarios,[]):priority_label{high:高,medium:中,low:低}type_label{positive:正向,negative:负向,boundary:边界}lines.append(f##{sc.get(scenario_id)}{sc.get(name)})lines.append(f- **类型**{type_label.get(sc.get(type,),sc.get(type))})lines.append(f- **优先级**{priority_label.get(sc.get(priority,low),低)})lines.append(f- **描述**{sc.get(description,)})ifsc.get(preconditions):lines.append(- **前置条件**)forpinsc.get(preconditions,[]):lines.append(f -{p})ifsc.get(steps):lines.append(- **操作步骤**)foridx,stepinenumerate(sc.get(steps,[]),1):lines.append(f{idx}.{step})lines.append(f- **预期结果**{sc.get(expected,)}\n)lines.append(---\n)withopen(output_file,w,encodingutf-8)asf:f.write(\n.join(lines))print(fMarkdown报告已生成:{output_file})现在一份需求进去出来三样东西JSON文件给下游模块用Pydantic对象在代码里用有类型安全Markdown报告给人看发给团队review常见问题Q1为什么输出JSON不直接输出代码JSON和代码的可读性不一样如果AI生成的代码有bug你得先看着代码改生成的策略漏了场景你还是得在代码里改写过代码的同学都体会过改别人代码的痛苦。JSON不一样你打开文件加一个场景填几个字段改完再让AI重新生成代码就行。Q2AI生成的测试策略能直接用吗不能。你得看一遍。有时候AI会漏场景有时候对业务规则理解偏了。但你review一个JSON比从零写快很多。核心原则就一条AI出初稿你做判断。总结这篇做了个东西意图理解模块。输入是一段需求描述输出是一份测试策略JSON。包含了三层东西核心调用把需求发给LLM返回结构化的JSON。输出校验。用Pydantic卡一道字段不对就报错确保下游拿到的数据是干净的。Markdown报告转成文档发给团队能看能改。确定了一个设计输出JSON不是直接出代码。因为可改、可复用、可审查。现在第一环搭好了之后我们就可以把意图理解和用例生成连起来需求进去测试代码出来。