Agent用例生成进阶:从测试策略到Pytest代码

📅 发布时间:2026/8/23 23:48:35
Agent用例生成进阶:从测试策略到Pytest代码 概述上一篇做了测试策略输入需求输出JSON。但CI流水线不认JSON它要的是pytest能跑的东西这篇就把测试策略转成Pytest代码。现在我们有策略策略里啥都有场景、预期、类型、优先级。把这些喂给AI需求进去代码出来中间不需要人动手。用例生成模块是做什么的输入测试策略JSON输出Pytest测试代码。具体点说输入是上一篇生成的策略功能名称、场景列表、步骤、预期结果。输出是一个完整的测试文件你拿过来就能用pytest命令跑。为什么要拆成两步把意图理解和用例生成合并成一步输入需求直接输出Pytest代码。听起来很爽少了一道工序但合在一起真的不好用。可能带来的问题出错了你找不到原因生成的代码挂了你不知道是需求理解错了还是代码生成环节出了岔子。日志里只有头和尾中间发生了什么是个黑盒。你想改没地方下手。策略生成错了你想手动改一下再重新生成。如果合并成一步你得重新跑一遍完整流程API费用再花一次。换个框架你得重写全部。同一个测试策略你可能想生成pytest版本的也可能想生成unittest版本的合并的话每种框架你得维护一套完整逻辑。拆开的话策略模块不用动只换生成模块就行。所以拆开更实用具体怎么做3.1 核心实现先写一个生成器类。# test_generation.pyimportjsonimportosfrompathlibimportPathfromtypingimportDict,Optionalfromdotenvimportload_dotenvfromopenaiimportOpenAIclassTestGenerationAgent: 用例生成Agent。 输入测试策略JSON和源代码输出Pytest测试代码。 def__init__(self,model:strgpt-4o):load_dotenv()self.clientOpenAI(api_keyos.getenv(OPENAI_API_KEY),base_urlos.getenv(OPENAI_BASE_URL,None))self.modelmodeldefgenerate(self,strategy:Dict,source_code:str,module_name:strapp)-str:根据测试策略和源代码生成Pytest测试代码。promptself._build_prompt(strategy,source_code,module_name)try:responseself.client.chat.completions.create(modelself.model,messages[{role:system,content:self._get_system_prompt()},{role:user,content:prompt}],temperature0.3)returnresponse.choices[0].message.contentexceptExceptionase:print(f用例生成失败:{e})returndef_get_system_prompt(self)-str:return 你是一位资深的测试开发工程师擅长用Pytest编写高质量的单元测试。 你的任务是根据提供的测试策略和源代码生成完整的Pytest测试代码。 要求 1. 测试代码必须覆盖策略中定义的所有场景 2. 使用pytest框架遵循最佳实践 3. 每个测试场景对应一个测试方法 4. 方法名用test_开头后面跟场景的英文标识 5. 使用pytest.raises处理异常场景 6. 添加适当的注释说明每个测试的用途 7. 代码风格清晰、规范 8. 只输出Python代码不要额外解释 def_build_prompt(self,strategy:Dict,source_code:str,module_name:str)-str:scenariosstrategy.get(test_scenarios,[])scenario_textjson.dumps(scenarios,indent2,ensure_asciiFalse)returnf#下面是待测试的源代码{source_code}#下面是测试策略包含需要覆盖的测试场景{scenario_text}# 请为 {module_name} 模块生成完整的Pytest测试代码。defsave_test_code(code:str,filepath:strtest_generated.py):withopen(filepath,w,encodingutf-8)asf:f.write(code)print(f测试代码已保存:{filepath})3.2 如何使用加载上一篇生成的测试策略传入源代码调用生成器。if__name____main__:# 加载上一篇生成的测试策略withopen(login_strategy.json,r,encodingutf-8)asf:strategyjson.load(f)# 被测试的源代码source_code # login.py class UserService: def __init__(self): self._users { 13800001111: {password: 123456, nickname: 张三, locked: False}, testexample.com: {password: 123456, nickname: 李四, locked: False}, } self._login_attempts {} self._locked_accounts {} def login(self, account: str, password: str) - dict: if account not in self._users: return {success: False, message: 账号不存在} user self._users[account] if user.get(locked, False): return {success: False, message: 账号已被锁定请联系客服} if account in self._locked_accounts: lock_time self._locked_accounts[account] if lock_time 0: return {success: False, message: 账号已被锁定30分钟} if user[password] ! password: self._login_attempts[account] self._login_attempts.get(account, 0) 1 if self._login_attempts[account] 5: self._locked_accounts[account] 30 return {success: False, message: 因多次登录失败账号已被锁定30分钟} return {success: False, message: 密码错误} self._login_attempts[account] 0 return { success: True, message: 登录成功, nickname: user[nickname] } agentTestGenerationAgent()test_codeagent.generate(strategy,source_code,login)iftest_code:save_test_code(test_code,test_login.py)print(\n生成的测试代码\n)print(test_code)else:print(生成失败)3.3 AI生成的代码运行上面的脚本test_login.py的内容大概是这样的。# test_login.pyimportpytestfromloginimportUserServiceclassTestUserLogin:用户登录功能的测试套件defsetup_method(self):每个测试方法执行前重置状态self.serviceUserService()deftest_login_with_phone_success(self):TC-001: 使用手机号正确登录resultself.service.login(13800001111,123456)assertresult[success]isTrueassertresult[message]登录成功assertresult[nickname]张三deftest_login_with_email_success(self):TC-002: 使用邮箱正确登录resultself.service.login(testexample.com,123456)assertresult[success]isTrueassertresult[message]登录成功assertresult[nickname]李四deftest_login_account_not_exist(self):TC-003: 登录失败-账号不存在resultself.service.login(13900001111,123456)assertresult[success]isFalseassertresult[message]账号不存在deftest_login_wrong_password(self):TC-004: 登录失败-密码错误resultself.service.login(13800001111,wrongpass)assertresult[success]isFalseassertresult[message]密码错误deftest_login_account_locked_by_admin(self):TC-005: 登录失败-账号被锁定self.service._users[13800001111][locked]Trueresultself.service.login(13800001111,123456)assertresult[success]isFalseassertresult[message]账号已被锁定请联系客服deftest_login_trigger_lock_after_5_failures(self):TC-006: 连续输错密码触发锁定account13800001111foriinrange(5):resultself.service.login(account,wrongpass)ifi4:assertresult[message]密码错误else:assertresult[message]因多次登录失败账号已被锁定30分钟assertresult[success]isFalsedeftest_login_paste_disabled(self):TC-007: 密码框禁止粘贴# 这个场景是前端约束单元测试测不了# 实际项目中需要UI自动化测试覆盖pytest.skip(前端约束需要UI自动化测试验证)deftest_login_response_time(self):TC-008: 登录接口响应时间importtime starttime.time()self.service.login(13800001111,123456)elapsed(time.time()-start)*1000assertelapsed500,f响应时间{elapsed}ms超过500ms策略里的8个场景变成了8个测试方法。每个方法对应一个场景断言也写好了。3.3 把两步串起来下面这个脚本把意图理解和用例生成串成一条线。# end_to_end.pyimportjsonfromintent_understandingimportIntentUnderstandingAgentfromtest_generationimportTestGenerationAgentdefrun_end_to_end(requirement:str,source_code:str,module_name:strapp):print(*50)print(Step 1: 意图理解)print(*50)intent_agentIntentUnderstandingAgent()strategyintent_agent.understand(requirement)iferrorinstrategy:print(f失败:{strategy[error]})returnprint(f生成{len(strategy.get(test_scenarios,[]))}个场景)withopen(strategy_output.json,w,encodingutf-8)asf:json.dump(strategy,f,indent2,ensure_asciiFalse)print(策略已保存: strategy_output.json)print(\n*50)print(Step 2: 用例生成)print(*50)gen_agentTestGenerationAgent()test_codegen_agent.generate(strategy,source_code,module_name)ifnottest_code:print(生成失败)returnwithopen(test_output.py,w,encodingutf-8)asf:f.write(test_code)print(测试代码已保存: test_output.py)print(\n*50)print(完成)print(*50)print(f场景数:{len(strategy.get(test_scenarios,[]))})print(下一步: pytest test_output.py -v)if__name____main__:requirement 需求用户登录功能 用户可以使用手机号或邮箱进行登录。 登录时需要验证密码是否正确。 登录成功后的行为 - 跳转到个人主页 - 在顶部显示用户昵称 登录失败的场景 - 手机号/邮箱不存在提示账号不存在 - 密码错误提示密码错误 - 账号被锁定提示账号已被锁定请联系客服 安全要求 - 连续输错5次密码后账号锁定30分钟 source_code class UserService: def __init__(self): self._users { 13800001111: {password: 123456, nickname: 张三, locked: False}, } self._login_attempts {} self._locked_accounts {} def login(self, account: str, password: str) - dict: if account not in self._users: return {success: False, message: 账号不存在} user self._users[account] if user.get(locked, False): return {success: False, message: 账号已被锁定请联系客服} if account in self._locked_accounts: return {success: False, message: 账号已被锁定30分钟} if user[password] ! password: self._login_attempts[account] self._login_attempts.get(account, 0) 1 if self._login_attempts[account] 5: self._locked_accounts[account] 30 return {success: False, message: 因多次登录失败账号已被锁定30分钟} return {success: False, message: 密码错误} self._login_attempts[account] 0 return {success: True, message: 登录成功, nickname: user[nickname]} run_end_to_end(requirement,source_code,login)跑完之后执行pytest test_output.py-v看到测试通过的那一刻整个流程就闭环了。需求进去代码出来中间不用你写一行。常见问题Q1生成的代码能直接用吗最好检查一遍。AI偶尔会犯错。import路径不对或者依赖的类名写错了。大问题不多小毛病难免。但比自己从头写还是能省了至少一个小时的时间Q2代码跑不通怎么办把报错信息贴给AI让它自己改。这是目前最省事的做法。把pytest的输出复制下来跟原始代码一起发给AI告诉它这段测试挂了帮我修一下。大部分情况下AI能自己搞定。Q3场景太多了生成慢怎么办拆成几批。如果一个策略有20多个场景你可以分两批生成。先让AI生成前10个保存。再生成后10个追加到同一个文件。这样每次请求的数据量小一些响应快也不容易超出AI的上下文窗口。Q4项目代码风格不一样怎么办把规范写进prompt里。比如你们项目要求测试类必须继承某个基类或者必须用特定的fixture。把这些要求写进系统提示词AI会照着做。如果你想做得更细致可以在项目里放一个规范文件每次生成之前让AI先读一遍。总结意图理解管出策略用例生成出代码中间用JSON对接。整个流程是这样的需求描述 → 测试策略 → Pytest代码 → pytest执行前三步我们已经能自动跑通了第四步就是pytest自己的事。