基于Autogen + LLM 自主生成测试用例
基于 AutoGen 框架的多智能体(Multi-Agent)测试用例自动生成系统。该系统能够从文本需求或图像需求中读取内容,并利用大模型(如 Qwen 或 DeepSeek)生成和审阅结构化的 Markdown 格式测试用例。
代码实现
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
File: testcase_generator_v3.py
Author: Rosaany
Created: 2025-05-22
Version: 2.0
Description:
- 从本地读取需求文档,支持txt、图像文件等格式
- 生成md等格式的测试用例
Dependencies:
Python > 3.10
- pillow: 11.2.1
- asyncio: 3.4.3
- autogen_agentchat: 0.5.6
- autogen_core: 0.5.6
- autogen_ext: 0.5.6
Usage:
License: MIT
"""
import datetime
import os
import base64
import asyncio
from typing import List, Generator, Any, Dict, Optional
from abc import ABC, abstractmethod
from PIL import Image as PILImage
from io import BytesIO
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import TextMentionTermination, MaxMessageTermination
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_core.models import ModelInfo
from autogen_core import Image as AutogenImage
from autogen_agentchat.messages import MultiModalMessage, TextMessage
class BaseModelClient(ABC):
"""所有大模型客户端的抽象基类"""
@abstractmethod
async def generate(self, messages: List[Dict[str, str]], **kwargs) -> str:
"""异步生成内容"""
pass
@abstractmethod
def get_model_info(self) -> Dict[str, Any]:
"""获取模型能力信息"""
pass
@abstractmethod
async def close(self):
"""关闭客户端连接"""
pass
class DeepSeekClient(BaseModelClient):
"""DeepSeek模型客户端"""
def __init__(self, api_key: str, model: str = "deepseek-chat"):
self.model = model
self.client = OpenAIChatCompletionClient(
model=model,
api_key=api_key,
model_info=ModelInfo(
vision=False, # DeepSeek-chat 默认为文本模型,vision 为 False
function_calling=True,
json_output=True,
family="deepseek",
structured_output=True,
multiple_system_messages=True
),
base_url="https://api.deepseek.com/v1"
)
async def generate(self, messages: List[Dict[str, str]], **kwargs) -> str:
"""异步生成内容"""
response = await self.client.create(messages=messages, **kwargs)
return response
def get_model_info(self) -> Dict[str, Any]:
"""获取模型能力信息"""
if hasattr(self.client.model_info, '__dict__'):
return self.client.model_info.__dict__
return {
"vision": False,
"function_calling": True,
"json_output": True,
"family": "deepseek",
"structured_output": True,
"multiple_system_messages": True
}
async def close(self):
await self.client.close()
class QwenClient(BaseModelClient):
"""Qwen模型客户端(兼容原有AutoGen接口)"""
def __init__(self, api_key: str, model: str = "qwen2.5-vl-72b-instruct"):
self.client = OpenAIChatCompletionClient(
model=model,
api_key=api_key,
model_info=ModelInfo(
vision=True,
function_calling=True,
json_output=True,
family="qwen",
structured_output=True,
multiple_system_messages=True
),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
async def generate(self, messages: List[Dict[str, str]], **kwargs) -> str:
response = await self.client.create(messages=messages, **kwargs)
return response
def get_model_info(self) -> Dict[str, Any]:
if hasattr(self.client.model_info, '__dict__'):
return self.client.model_info.__dict__
return {
"vision": True,
"function_calling": True,
"json_output": True,
"family": "qwen",
"structured_output": True,
"multiple_system_messages": True
}
async def close(self):
await self.client.close()
class MultiModelClient:
"""多模型统一入口"""
MODEL_MAPPING = {
"deepseek": DeepSeekClient,
"qwen": QwenClient
}
def __init__(self, model_type: str, api_key: str, model: Optional[str] = None):
if model_type not in self.MODEL_MAPPING:
raise ValueError(f"不支持模型类型: {model_type},可选: {list(self.MODEL_MAPPING.keys())}")
model = model or {
"deepseek": "deepseek-chat",
"qwen": "qwen2.5-vl-72b-instruct"
}[model_type]
self.client = self.MODEL_MAPPING[model_type](api_key, model)
info_dict = self.client.get_model_info()
self.model_info = ModelInfo(**info_dict)
async def create(self, messages: List[Dict[str, str]], **kwargs) -> Any:
"""AutoGen标准接口方法"""
if 'max_tokens' in kwargs:
kwargs['max_tokens'] = kwargs.pop('max_tokens')
result = await self.client.generate(messages, **kwargs)
return result
def get_model_info(self) -> Dict[str, Any]:
return self.client.get_model_info()
async def close(self):
await self.client.close()
# 添加AutoGen需要的其他属性
@property
def model(self) -> str:
return self.client.model if hasattr(self.client, 'model') else "unknown"
class ImageInputProcessor:
"""
图像处理模块:支持读取单张或多张图像,转换为Autogen格式
"""
@staticmethod
def encode_image_to_base64(image_path: str) -> str:
"""
将图像文件转换为base64字符串,供HTTP传输或多模态模型识别使用
Args:
image_path (str): 图像文件的路径
Returns:
str: base64格式的图像内容,自动拼接data前缀
Examples:
base64_str = ImageInputProcessor.encode_image_to_base64("img.png")
"""
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode()
@staticmethod
def decode_base64_to_bytes(base64_str: base64) -> bytes:
return base64.b64decode(base64_str)
@staticmethod
def read_multi_images_from_directory(directory: str) -> Generator[tuple[str, str]]:
"""
读取目录下所有图像文件并转换为生成器的str
Args:
directory (str): 目录路径
Returns:
Generator[tuple[str, str]]: 多张图像的(图像路径str, 图像名字str)
"""
for file in os.listdir(directory):
path = os.path.join(directory, file)
if path.lower().endswith((".png", ".jpg", ".jpeg")):
yield path, file
@staticmethod
def build_autogen_image(image_path: str) -> AutogenImage:
"""
将本地图像 -> base64 -> bytes -> 加载为 Autogen 支持的 Image 对象
Args:
image_path (str): 图像路径
Returns:
AutogenImage: 图像对象
"""
# https://microsoft.github.io/autogen/stable/reference/python/autogen_core.html#autogen_core.Image
base64_str = ImageInputProcessor.encode_image_to_base64(image_path)
bytes_image_data = ImageInputProcessor.decode_base64_to_bytes(base64_str)
pil_image = PILImage.open(BytesIO(bytes_image_data))
return AutogenImage.from_pil(pil_image)
class TextRequirementProcessor:
"""
文本需求输入模块:读取多行文本需求
"""
@staticmethod
def read_requirements(file_path: str) -> Generator[str]:
"""
读取文本文件的每一行作为一条需求
Args:
file_path (str): 文本文件路径
Returns:
Generator[str]: 每一行的需求内容
"""
with open(file_path, "r", encoding="utf-8") as f:
for line in f:
yield line.strip()
class AgentFactory:
"""
Agent 生成工厂,支持自定义提示词
"""
@staticmethod
def create_agents(writer_prompt: str, reviewer_prompt: str, model_client):
writer = AssistantAgent(
name="writer",
description="负责根据需求生成测试用例",
system_message=writer_prompt,
model_client=model_client,
)
reviewer = AssistantAgent(
name="reviewer",
description="负责审阅测试用例的完整性和专业性",
system_message=reviewer_prompt,
model_client=model_client,
)
return writer, reviewer
class OutputWriter:
"""
输出结果写入模块:支持输出为文本(md)、word格式
"""
@staticmethod
def write_to_text(content: str, format_type = 'md', **kwargs: Any):
"""
将模型输出写入对应格式的文件中
Args:
content (str): 要写入的内容
format_type (str): 支持格式 'md', 'docx'
Args in kwargs:
identifier (str): 自定义输出文件名前缀,默认 'image'
output_dir (str): 自定义输出目录,默认 '. 即当前目录'
Returns:
None
Examples:
OutputWriter.write_to_text("测试内容", format_type="md", identifier="login")
Default output: '.\testcase_text_20250522_173342.md'
"""
_identifier = kwargs.get("identifier", "text")
_output_dir = kwargs.get("output_dir", ".")
if _output_dir != '.' and os.path.isfile(_output_dir):
_output_dir = os.path.dirname(_output_dir)
now = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filename = rf"{_output_dir}\testcase_{_identifier}_{now}.{format_type}"
with open(filename, "w", encoding="utf-8") as f:
f.write(content)
print(f"✅ 输出写入文件: {filename}")
class TestCaseAgentOrchestrator:
"""
多智能体交互与编排模块,基于大模型生成测试用例
"""
def __init__(self, model_type: str = "qwen", api_key: str=None, writer_prompt: str=None, reviewer_prompt: str=None,
output_format: str = "md", model: Optional[str] = None):
self.writer_prompt = writer_prompt
self.reviewer_prompt = reviewer_prompt
self.output_format = output_format
self.model_client = MultiModelClient(
model_type=model_type,
api_key=api_key,
model=model
)
async def run_from_text(self, task: str, **kwargs: Any):
writer, reviewer = AgentFactory.create_agents(self.writer_prompt, self.reviewer_prompt, self.model_client)
termination = TextMentionTermination("APPROVE") | MaxMessageTermination(10)
group_chat = RoundRobinGroupChat(
[writer, reviewer],
termination_condition=termination,
max_turns=12
)
stream = group_chat.run_stream(task=task)
buffer = ""
async for msg in stream:
# if hasattr(msg, "source") and hasattr(msg, "content"):
if isinstance(msg, TextMessage):
print(f"\n---------- TextMessage ({msg.source}) ----------")
print(msg.content)
buffer += f"[{msg.source}]\n{msg.content}\n\n"
OutputWriter.write_to_text(buffer, format_type=self.output_format, **kwargs)
async def run_from_image(self, image_path: str, **kwargs: Any):
print(f"加载到:{image_path}")
writer, reviewer = AgentFactory.create_agents(self.writer_prompt, self.reviewer_prompt, self.model_client)
image_obj = ImageInputProcessor.build_autogen_image(image_path)
multimodal_input = MultiModalMessage(
source="user",
content=["请基于该图像分析其功能,并生成相应的测试用例", image_obj]
)
termination = TextMentionTermination("APPROVE") | MaxMessageTermination(10)
group_chat = RoundRobinGroupChat(
[writer, reviewer],
termination_condition=termination,
max_turns=12
)
buffer = ""
stream = group_chat.run_stream(task=multimodal_input)
async for message in stream:
if isinstance(message, TextMessage):
sender = message.source
print(f"\n---------- TextMessage ({sender}) ----------")
print(message.content)
buffer += f"[{sender}]\n{message.content}\n\n"
OutputWriter.write_to_text(buffer, format_type=self.output_format, **kwargs)
async def close(self):
"""关闭底层模型客户端,释放资源。"""
if hasattr(self.model_client, 'close') and callable(self.model_client.close):
await self.model_client.close()
代码解释
🔷 模型客户端层
-
BaseModelClient (抽象基类)
- 定义了所有客户端必须实现的三个抽象方法:generate (异步内容生成), get_model_info (获取模型能力), 和 close (关闭连接),这是实现模型扩展性的关键。
-
QwenClient
-
实现了 BaseModelClient 接口
-
内部封装了 AutoGen 的 OpenAIChatCompletionClient,通过设置 DashScope 的兼容模式 URL (https://dashscope.aliyuncs.com/compatible-mode/v1) 来接入 Qwen 模型,实现对 AutoGen 框架的复用。
-
支持多模态 (Vision=True),通过 AutoGen 传递图像。
-
-
MultiModelClient
-
多模型类,通过 MODEL_MAPPING 字典根据 model_type(如 'qwen', 'deepseek')动态实例化具体的客户端。
-
将底层客户端的能力信息(get_model_info)包装成 AutoGen 所需的 ModelInfo 对象。
-
提供了 AutoGen 期望的 create 方法,作为 Agent 与大模型通信的桥梁。
-
🧪 输入处理层
-
ImageInputProcessor
-
提供了一系列静态方法处理图像文件:
-
encode_image_to_base64: 图像文件 -> Base64 字符串。
-
build_autogen_image: 最核心方法,将本地图像路径转化为 AutoGen AutogenImage 对象,用于多模态消息。
-
-
-
TextRequirementProcessor
- read_requirements: 从文本文件逐行读取需求。
🤖 智能体与编排层
-
AgentFactory
-
工厂方法 create_agents:根据传入的提示词(Prompt)和模型客户端,创建两个核心智能体:
-
writer (测试用例编写者)
-
reviewer (测试用例审阅专家)
-
-
TestCaseAgentOrchestrator (核心)
-
初始化时创建 MultiModelClient 和 writer/reviewer 的提示词。
-
run_from_text: 处理文本任务。构建 RoundRobinGroupChat,使用 TextMessage 启动聊天。
-
run_from_image: 处理图像任务。利用 ImageInputProcessor 转换图像,构建 MultiModalMessage 启动聊天。
-
聊天控制: 使用 RoundRobinGroupChat 实现 Agent 轮流发言,通过 TextMentionTermination("APPROVE") 或 MaxMessageTermination(10) 来终止多智能体协作。
-
💾 输出处理层
-
OutputWriter
- write_to_text: 将 Agent 协作的完整输出 (buffer) 写入到带有时间戳和标识符的文件中(默认为 .md 格式)。
使用 Qwen 大模型生成测试用例(输入需求)
在testcase_generator_v3.py文件加入main() 函数
if __name__ == "__main__":
default_writer_prompt = ("""
你是一名测试工程师,根据功能需求编写详细、结构化的测试用例,格式为md格式,包括用例名称、所属模块、前置条件、
测试步骤、预期结果、测试目的,用例格式遵循以下标准:
| 用例名称 | 所属模块 | 前置条件 | 测试步骤 | 预期结果 | 测试目的 | 测试类型 | 用例等级 |
|----------|----------|----------|----------|----------|---------|---------|--------|
用例等级划分标准:
P1-产品核心功能-此部分测试用例如果失败会阻碍大部分其他测试用例的验证;路径最浅,最常使用,如一级界面功能
P2-产品重要功能-用户应用场景、使用频率较高的常用功能;二级界面或一级界面中不常用功能或优化建议;影响主流程的边界、界面、交互及优化建议等
P3-产品一般功能-使用频率不高,不重要的功能;使用路径较深,不易发现,三级界面及以上的功能;不影响主流程的异常测试,边界、中断、断网、容错、
界面/交互,优化建议、功能优化建议
P4-产品非功能问题-非功能验证,不影响功能使用;不常被执行,改动频率低,路径较深
所属模块:存在子模块,用\分割
"""
)
default_reviewer_prompt = ("""
你是一名资深的测试专家,负责检查测试用例是否覆盖充分、逻辑清晰、表达规范,并反馈意见。如果覆盖充分,请回复 'APPROVE',用例补充与优化
遵循以下标准:
| 用例名称 | 所属模块 | 前置条件 | 测试步骤 | 预期结果 | 测试目的 | 测试类型 | 用例等级 |
|----------|----------|----------|----------|----------|---------|---------|--------|
"""
)
orchestrator = TestCaseAgentOrchestrator(
model_type='qwen',
api_key=os.environ.get("QWEN_API_KEY"),
writer_prompt=default_writer_prompt,
reviewer_prompt=default_reviewer_prompt,
output_format="md"
)
async def execute():
# 使用Qwen模型
qwen_orchestrator = TestCaseAgentOrchestrator(
model_type="qwen",
api_key=os.environ.get("QWEN_API_KEY"),
writer_prompt=default_writer_prompt,
reviewer_prompt=default_reviewer_prompt
)
await qwen_orchestrator.run_from_text("测试登录功能")
await qwen_orchestrator.close()
asyncio.run(execute())
运行效果

使用 Qwen 大模型生成测试用例(输入需求文档txt格式)
修改testcase_generator_v3.py文件main() 下的execute()方法
async def execute():
# 示例:使用文本需求生成测试用例
doc = r"D:\PRD\需求文档.txt"
requirements = TextRequirementProcessor.read_requirements(doc)
for req in requirements:
print(f"\n===== 当前需求: {req} =====")
await orchestrator.run_from_text(task=req, identifier=req, output_dir=doc)output_dir=doc)
await orchestrator.model_client.close()
asyncio.run(execute())
运行效果



使用 Qwen 大模型生成测试用例(输入需求文档,图像格式,支持单张)
修改testcase_generator_v3.py文件main() 下的execute()方法
async def execute():
#示例:使用图像生成测试用例
doc_images = r"D:\PRD\doc_images\流程知识库管理_01.png"
await orchestrator.run_from_image(doc_images, output_dir=doc_images)
await orchestrator.model_client.close()
asyncio.run(execute())
运行效果

使用 Qwen 大模型生成测试用例(输入需求文档,图像格式,支持多张)
修改testcase_generator_v3.py文件main() 下的execute()方法
async def execute():
# 示例:使用多张图像生成测试用例
doc_dirname = r"D:\PRD\doc_images"
for image_path, filename in ImageInputProcessor.read_multi_images_from_directory(directory=doc_dirname):
await orchestrator.run_from_image(image_path, identifier=filename, output_dir=doc_dirname)
await orchestrator.model_client.close()
asyncio.run(execute())
使用 DeepSeek 大模型生成测试用例
参考Qwen大模型实现,修改main() 下的TestCaseAgentOrchestrator的model_type和api_key即可, 如:
...
orchestrator = TestCaseAgentOrchestrator(
model_type='deepseek',
api_key=os.environ.get("DEEPSEEK_API_KEY"),
...
...
其中 DEEPSEEK_API_KEY 需提前准备好,并保存到系统变量中。

浙公网安备 33010602011771号