写点什么

AI 实践探索:辅助生成测试用例

作者:巫山老妖
  • 2025-05-10
    广东
  • 本文字数:4028 字

    阅读完需:约 13 分钟

背景

目前我们的测试用例主要依赖人工生成和维护,AI 时代的来临,我们也在思考“AI 如何赋能业务”,提出了如下命题:


“探索通过 AI 辅助生成测试用例,完成从需求到测试用例生成的穿刺”。

目标

  • 找全测试路径

  • 辅助生成测试用例

实践案例:登录注册流程

自然语言描述需求

需求名称:注册登录流程


需求描述:


1、注册和登录在同一个页面,有 2 个按钮,一个注册,一个登录,用户输入用户名、密码进行登录或者注册


2、首页:加载一张图,有个退出按钮,点击则退出首页


注:这里只是为了验证思路,需求描述会比较简单,实际需求考虑会更完善。

如何找全测试路径

使用 LLM 生成 mermaid 格式的状态机描述

使用Dify 搭建的工作流:



将前面的需求描述作为输入参数,提供 Prompt 模板告诉 LLM,如下所示:



LLM 生成的 mermaid 状态机描述:


stateDiagram-v2    [*] --> Unregistered    Unregistered --> Registering: start_register    Registering --> Unregistered: register_failed    Registering --> LoggingIn: register_success    Unregistered --> LoggingIn: start_login    LoggingIn --> Unregistered: login_failed    LoggingIn --> LoggedIn: login_success    LoggedIn --> Unregistered: logout    LoggedIn --> [*]: exit
复制代码


Markdown 对 mermaid 支持友好,可以直接渲染成状态机图:



这里选择 Mermaid 来描述状态机的理由,主要是 Mermaid 天然适合文档化,代码轻量且无额外依赖,无需处理图片格式的一些问题。


参考:AI大模型生成的图表为什么倾向使用Mermaid格式?

使用 AI 帮我们开发工具

前面通过 LLM 能够帮我们理解需求生成状态机图,如果想基于状态机找全测试路径,我们尝试使用 AI 编程工具来辅助生成规则工具,来确保每次遍历的路径是一致的。


比如 Cursor:



通过多轮的对话和人工修正,Cursor 能够很高效的帮助我生成符合预期的代码,但仍需要人工去验证和调试。


核心路径生成算法:


from typing import List, Dict, Setfrom abc import ABC, abstractmethod
class PathGeneratorBase(ABC): def __init__(self): self.graph = {} self.paths = [] self.events = {} @abstractmethod def parse_input(self): """解析输入源(Mermaid或SCXML)""" pass def generate_paths(self, max_depth: int = 15) -> List[List[str]]: """通用的路径生成算法""" paths = [] start = self._find_start_state() visited_states = set() def dfs(current: str, path: List[str]): if len(path) > max_depth: return current_transitions = self._get_transitions(current) if self._should_terminate(current, path, current_transitions): paths.append(path[:]) return visited_states.add(current) for next_state in current_transitions: dfs(next_state, path + [next_state]) visited_states.remove(current) dfs(start, [start]) return self._deduplicate_paths(paths) def _find_start_state(self) -> str: """查找起始状态""" if 'START' in self.graph: return 'START' in_degrees = self._calculate_in_degrees() for node, degree in in_degrees.items(): if degree == 0: return node return None def _get_transitions(self, state: str) -> List[str]: """获取状态的所有可能转换""" if state not in self.graph: return [] return [target for target in self.graph[state]] def _should_terminate(self, current: str, path: List[str], transitions: List[str]) -> bool: """判断是否应该终止当前路径""" return len(path) > 1 and (not transitions or current in path[:-1]) def _deduplicate_paths(self, paths: List[List[str]]) -> List[List[str]]: """去除重复路径""" unique_paths = [] path_strings = set() for path in sorted(paths, key=len): path_str = "->".join(path) if path_str not in path_strings: path_strings.add(path_str) unique_paths.append(path) return unique_paths def calculate_coverage(self) -> Dict: """计算测试覆盖率""" all_states = set(self.graph.keys()) all_transitions = set() covered_states = set() covered_transitions = set() for path in self.paths: covered_states.update(path) for i in range(len(path) - 1): transition = (path[i], path[i + 1]) covered_transitions.add(transition) all_transitions.add(transition) return { "state_coverage": len(covered_states) / len(all_states) * 100, "transition_coverage": len(covered_transitions) / len(all_transitions) * 100 }
复制代码


根据路径生成算法遍历生成的路径索引:


如何生成测试用例

用例关键要素

  • 前置条件:描述复现测试场景所需的条件;

  • 操作步骤:描述测试场景下用户的操作行为;

  • 预期结果:描述测试场景下,用户执行完操作预期得到的结果。


示例:


LLM 生成测试用例


工作流设计:


  1. 输入需求描述和测试路径

  2. 设计 prompt 提示词,要求 LLM 按固定格式输出测试用例


生成的测试用例结果示例:


[  {    "precondition": "用户未注册,系统处于未登录状态",    "operation_procedure": [      "1. 访问系统首页",      "2. 输入用户名:a1_(3字符)",      "3. 输入密码:Passw0rd!",      "4. 点击'注册'按钮"    ],    "expected_result": [      "注册失败,提示'用户名长度需在4-20字符之间'",      "页面停留在注册/登录页面"    ]  },  {    "precondition": "用户未注册,系统处于未登录状态",    "operation_procedure": [      "1. 访问系统首页",      "2. 输入用户名:existing_user",      "3. 输入密码:Passw0rd!",      "4. 点击'注册'按钮",      "5. 再次输入相同用户名和密码点击'注册'按钮"    ],    "expected_result": [      "第二次注册时提示'用户名已存在'",      "页面停留在注册/登录页面"    ]  },  {    "precondition": "用户未注册,系统处于未登录状态",    "operation_procedure": [      "1. 访问系统首页",      "2. 输入用户名:user😊name",      "3. 输入密码:Passw0rd!",      "4. 点击'注册'按钮"    ],    "expected_result": [      "注册失败,提示'用户名包含非法字符'",      "页面停留在注册/登录页面"    ]  },  {    "precondition": "用户未注册,系统处于未登录状态",    "operation_procedure": [      "1. 访问系统首页",      "2. 输入不存在的用户名:new_user",      "3. 输入错误密码:wrongPass",      "4. 点击'登录'按钮"    ],    "expected_result": [      "登录失败,提示'用户名或密码错误'",      "页面停留在注册/登录页面"    ]  },  {    "precondition": "用户已注册,系统处于未登录状态",    "operation_procedure": [      "1. 访问系统首页",      "2. 输入已注册用户名:valid_user",      "3. 输入正确密码:Passw0rd!",      "4. 点击'登录'按钮"    ],    "expected_result": [      "登录成功,跳转至带图片的首页",      "页面显示退出按钮"    ]  },  {    "precondition": "新用户完成注册,系统处于未登录状态",    "operation_procedure": [      "1. 访问系统首页",      "2. 输入符合要求的用户名:new_user_123",      "3. 输入符合要求的密码:Passw0rd!",      "4. 点击'注册'按钮",      "5. 使用相同凭证点击'登录'按钮"    ],    "expected_result": [      "注册成功后自动清除表单",      "登录成功跳转至首页"    ]  },  {    "precondition": "用户已登录,处于首页",    "operation_procedure": [      "1. 点击'退出'按钮"    ],    "expected_result": [      "返回注册/登录页面",      "用户会话终止"    ]  },  {    "precondition": "用户未注册,系统处于未登录状态",    "operation_procedure": [      "1. 访问系统首页",      "2. 输入有效用户名:valid_user",      "3. 输入无效密码:password",      "4. 点击'注册'按钮"    ],    "expected_result": [      "注册失败,提示'密码需包含大写字母、小写字母、数字和特殊字符'",      "页面停留在注册/登录页面"    ]  }]
复制代码


这里可以进一步将 json 数据转成 xmind 或 excel 格式的文件。

收益

价值思考? 几个方向:


  • AI 辅助建模(状态机、类图),提升研发需求建模效率和规范

  • 释放助理生成测试用例的生产力,转变为 review/修改用例的角色

  • AI 辅助生成符合业务规则的可测试数据,提升构造数据的效率

经验总结

  • 使用 AI 帮助我们进行自然语言推理和图解析

  • 要让 AI 生成的测试用例更准确,需要精确描述需求,对需求进行建模,描述清楚业务规则

  • 使用 AI 编程工具帮助我们开发工具,比如有明确规则的工具开发

  • 通过搭建 AI 工作流完成需求穿刺

附录


发布于: 刚刚阅读数: 2
用户头像

巫山老妖

关注

不总结哪来的经验,不分享经验有何用? 2018-02-22 加入

专注于移动开发领域,公众号【巫山老妖】。

评论

发布
暂无评论
AI实践探索:辅助生成测试用例_测试用例_巫山老妖_InfoQ写作社区