掘金 人工智能 05月07日 10:43
AI 实践探索:辅助测试用例生成
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文探讨了如何利用AI辅助生成测试用例,实现从需求到测试用例生成的全流程穿刺。文章以登录注册流程为例,详细介绍了如何使用LLM生成mermaid格式的状态机描述,从而找全测试路径。同时,利用AI编程工具辅助生成规则工具,确保路径遍历的一致性。最后,通过设计prompt提示词,要求LLM按固定格式输出测试用例,并可将json数据转成xmind或excel格式的文件,最终实现AI辅助建模、释放测试用例生产力以及提升构造数据的效率。

🗺️ 使用LLM生成mermaid格式的状态机描述,将需求转化为清晰的状态机图,从而找全测试路径,为后续测试用例生成奠定基础。Mermaid代码轻量且无额外依赖,天然适合文档化。

🤖 利用AI编程工具(如Cursor)辅助开发工具,生成符合预期的代码,实现核心路径生成算法。通过多轮对话和人工修正,提高代码生成效率,但仍需人工验证和调试。

🧪 通过搭建AI工作流,输入需求描述和测试路径,设计prompt提示词,要求LLM按固定格式输出测试用例。生成的测试用例包含前置条件、操作步骤和预期结果,可进一步转换为xmind或excel格式的文件。

📈 AI辅助建模(状态机、类图)可以提升研发需求建模效率和规范,同时释放助理生成测试用例的生产力,使其转变为review/修改用例的角色。此外,AI还能辅助生成符合业务规则的可测试数据,提升构造数据的效率。

背景

目前我们的测试用例主要依赖人工生成和维护,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支持友好,可以直接渲染成状态机图:

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

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

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

使用AI帮我们开发工具

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

比如Cursor:

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

核心路径生成算法:

from typing import List, Dict, Setfrom abc import ABC, abstractmethodclass 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 生成测试用例

工作流设计:

    输入需求描述和测试路径设计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格式的文件。

收益

价值思考? 几个方向:

经验总结

附录

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

AI测试 测试用例生成 LLM 状态机 AI建模
相关文章