TestHub智能测试管理平台框架介绍

开源AI测试平台TestHub部署与UI自动化实战指南 自动化测试是提升软件质量与研发效能的关键实践,其核心在于通过脚本模拟用户操作,实现测试用例的自动执行与验证。传统UI自动化框架如Selenium和Playwright提供了强大的浏览器操控能力,但脚本编写、维护及测试数据管理成本高昂。AI技术的引入正重塑这一流程,通过智能解析需求、自动生成测试用例与数据,显著降低了自动化门槛并提升了测试设计的智能化水平。TestHub作为一个集成AI能力的开源测试管理平台,正是这一趋势下的工程实践典范。它结合了**大语言模型(LLM)** 与**可视化编排**,为测试工程师 阅读详情

一、项目介绍

 基于 Python + Django + DRF + Vue3 + MySQL + Celery + Redis + Playwright + Airtest + Allure

基于 Django REST Framework + Vue3 前后端分离架构搭建的 TestHub 智能测试管理平台,
把测试工作中分散的工具整合到统一平台:在线编写用例、用例评审、接口测试、
Web UI 自动化、Android APP 自动化、AI 需求分析与用例生成、测试报告集成。

平台覆盖从需求文档到测试报告的全链路:上传 PDF/Word/TXT 需求文档 →
调用大模型解析业务需求 → 批量生成测试用例 → 进入评审流程 →
关联到接口/UI/APP 自动化执行 → 生成 Allure 报告,实现测试资产的一站式管理。技术栈明细

二、项目结构说明

testhub_platform/                    # 项目根目录
├── backend/                         # Django 项目配置包
│   ├── __init__.py                  # PyMySQL 伪装为 MySQLdb 的兼容入口
│   ├── settings.py                  # 全局配置(数据库/JWT/DRF/Celery/日志)
│   ├── urls.py                      # 根路由:统一按 /api/<模块>/ 分发
│   ├── wsgi.py                      # 生产 HTTP 入口(gunicorn 加载)
│   ├── asgi.py                      # WebSocket 入口(Daphne 加载)
│   ├── celery.py                    # Celery 应用初始化
│   └── middleware.py                # 自定义中间件(CSRF 控制)
├── apps/                            # 15 个业务应用(模块化拆分)
│   ├── users/                       # 自定义用户模型 + JWT 登录/登出
│   ├── projects/                    # 项目、成员角色、环境配置
│   ├── testcases/                   # 测试用例、步骤、附件、评论
│   ├── testsuites/                  # 测试套件
│   ├── executions/                  # 测试计划、执行、结果
│   ├── reports/                     # 测试报告
│   ├── reviews/                     # 用例评审流程、模板、检查项
│   ├── versions/                    # 版本管理
│   ├── assistant/                   # Dify AI 助手
│   ├── requirement_analysis/        # AI 需求分析 + 用例生成 + AI 模型配置
│   ├── api_testing/                 # 接口测试(HTTP/WebSocket)
│   ├── ui_automation/               # Web UI 自动化(Selenium/Playwright)
│   ├── app_automation/              # Android APP 自动化(Airtest)
│   ├── core/                        # 公共能力:变量解析/定位策略/定时调度
│   └── data_factory/                # 数据工厂:造数、编码、加密、JSON 工具
├── frontend/                        # Vue3 前端
│   ├── src/
│   │   ├── views/                   # 84 个页面(按模块分目录)
│   │   ├── api/                     # 接口封装,按模块拆分
│   │   ├── stores/                  # Pinia 状态管理(user / app)
│   │   ├── router/                  # Vue Router 路由与登录守卫
│   │   ├── utils/api.js             # axios 实例:JWT 注入 + 自动刷新
│   │   └── layout/                  # 整体布局
│   ├── vite.config.js               # Vite 配置(端口 3000,代理 /api → 8000)
│   └── package.json
├── docs/                            # 20 篇功能与排查文档
├── allure/                          # Allure 命令行工具
├── media/                           # 上传文件与报告产物
├── logs/                            # 运行日志
├── manage.py                        # 开发期命令入口
├── requirements.txt                 # 依赖清单
├── .env                             # 环境变量(数据库/密钥/Redis)
└── start-testhub.ps1                # 一键启动脚本(数据库/后端/前端/Redis)

业务应用内部结构:

apps/api_testing/
├── models.py            # 数据模型
├── serializers.py       # 校验 + 序列化
├── views.py             # 业务逻辑
├── urls.py              # 路由
├── admin.py             # 后台注册
├── request_utils.py     # 请求组装与响应提取
├── utils.py             # 断言执行
├── variable_resolver.py # 变量解析(委托 core)
└── tasks.py             # Celery 异步任务

三、核心代码

1.程序主入口与请求链路

Django四个入口:

入口用途
manage.py开发期命令(runserver/ migrate/ shell)
backend/wsgi.py生产 HTTP(gunicorn 加载)
backend/asgi.pyWebSocket(Daphne 加载)
backend/celery.py异步任务

代码如下:manage.py

#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


def main():
    """Run administrative tasks."""
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()

代码说明

设置配置模块

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
  • 告诉 Django 到哪个模块读取配置。
  •  用 setdefault 而不是直接赋值,是为了允许环境变量覆盖,生产环境可以通过外部变量切换不同 settings,不用改代码。

Django 命令行框架

execute_from_command_line(sys.argv)
  • 把命令行参数交给 Django 解析并执行对应命令
  • 万能入口:runserver、migrate、createsuperuser、shell全部走这里,不只是"启动服务器"

判断__name__ == '__main__:

  • 保证该文件被其他模块 import 时不会自动执行命令行逻辑

启动链路

manage.py:9   设置 DJANGO_SETTINGS_MODULE='backend.settings'
manage.py:13  execute_from_command_line(sys.argv)
      ↓
backend/settings.py   读取配置
   ├ MIDDLEWARE(CORS / CSRF / Session / 认证)
   ├ ROOT_URLCONF = 'backend.urls'      ← 真正的路由中枢
   ├ AUTH_USER_MODEL = 'users.User'     ← 自定义用户模型
   └ CELERY_BROKER_URL ← 读 .env 的 REDIS_URL
      ↓
backend/urls.py       按路径分发到各模块
      ↓
apps/<模块>/urls.py → views.py → serializers.py → models.py → MySQL

manage.py 只是启动器,真正决定应用行为的是 settings.py(配置)和 urls.py(路由)

路由分发:backend/urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
    path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
    path('api/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),

    path('api/auth/', include('apps.users.urls')),
    path('api/projects/', include('apps.projects.urls')),
    path('api/testcases/', include('apps.testcases.urls')),
    path('api/requirement-analysis/', include('apps.requirement_analysis.urls')),
    path('api/ui-automation/', include('apps.ui_automation.urls')),
    path('api/app-automation/', include('apps.app_automation.urls')),
    path('api/data-factory/', include('apps.data_factory.urls')),
    ...
]
  • 所有接口统一以 /api/为前缀,根据 URL 查找对应代码目录。

2. 统一变量解析引擎(platforms 核心亮点)

接口测试和 UI 自动化都需要动态参数:随机手机号、时间戳、加密串等。
平台用一套 ${function_name(args)} 语法统一解决。

代码如下:apps/core/variable_resolver.py

# 工具名 → (工具类别, 真实实现函数)
TOOL_FUNCTIONS = {
    # ---------- 随机工具 ----------
    'random_int': ('random', RandomTools.random_int),
    'random_uuid': ('random', RandomTools.random_uuid),
    'random_date': ('random', RandomTools.random_date),
    # ---------- 测试数据工具 ----------
    'random_phone': ('test_data', TestDataTools.generate_chinese_phone),
    'generate_id_card': ('test_data', TestDataTools.generate_id_card),
    # ---------- 编码工具 ----------
    'base64_encode': ('encoding', EncodingTools.base64_encode),
    'generate_qrcode': ('encoding', EncodingTools.generate_qrcode),
    # ---------- 加密工具 ----------
    'md5': ('encryption', EncryptionTools.md5_hash),
    'sha256_hash': ('encryption', EncryptionTools.sha256_hash),
    # ---------- 时间日期函数(本地实现)----------
    'timestamp': ('time', '_time_timestamp'),
    'date': ('time', '_time_date'),
    ...
}


class VariableResolver:
    """统一变量解析器 - 使用数据工厂工具"""

    functions = TOOL_FUNCTIONS

    def __init__(self):
        # 类别 → 参数翻译器
        self.BUILDERS = {
            'random': self._build_random_kwargs,
            'test_data': self._build_test_data_kwargs,
            'string': self._build_string_kwargs,
            'encoding': self._build_encoding_kwargs,
            'encryption': self._build_encryption_kwargs,
            'crontab': self._build_crontab_kwargs,
        }

    def resolve(self, text):
        """解析文本中的动态函数占位符

        Args:
            text: 包含动态函数的文本,如 "Hello ${random_string(8)}"
        Returns:
            解析后的文本
        """
        if not isinstance(text, str):
            return text

        # 匹配 ${function_name(args)} 模式
        pattern = r'\$\{([^}]+)\}'

        def replace_func(match):
            expression = match.group(1)
            try:
                return str(self._evaluate_expression(expression))
            except Exception as e:
                if isinstance(e, UnicodeEncodeError):
                    return match.group(0)
                try:
                    print(f"[WARNING] Variable resolution failed: ${{{expression}}} - {str(e)}")
                except UnicodeEncodeError:
                    print(f"[WARNING] Variable resolution failed: ${{{expression}}}")
                return match.group(0)

        return re.sub(pattern, replace_func, text)

    def _evaluate_expression(self, expression):
        """评估单个表达式,如 "random_int(100, 200)" 或 "timestamp()" """
        match = re.match(r'(\w+)\((.*)\)', expression.strip())
        if not match:
            # 无参数函数
            func_name = expression.strip()
            args = []
        else:
            func_name = match.group(1)
            args_str = match.group(2)
            args = self._parse_args(args_str)

        # 只查一次表:函数名 → (调用类别, 实现函数)
        spec = TOOL_FUNCTIONS.get(func_name)
        if spec is None:
            return _Unresolved(func_name)

        kind, tool_func = spec
        tool_func = _load(tool_func)
        if kind == 'time':
            # 时间函数是本地实现,直接按 (func_name, args) 调用
            return tool_func(self, func_name, args)
        builder = self.BUILDERS.get(kind)
        if builder is None:
            return _Unresolved(func_name)
        return _run_tool(func_name, args, tool_func, builder)

方法说明:

单一登记表(注册表模式)

TOOL_FUNCTIONS = {
    'random_int': ('random', RandomTools.random_int),
    ...
}
  • 用字典隐射函数名映射到“类别+真是实现函数”
  • 新增函数只需注册一行,不改解析逻辑(对扩展开放、对修改封闭)
  • 所有实现最终都委托给 data_factory`的各类工具类

参数解析:

match = re.match(r'(\w+)\((.*)\)', expression.strip())
  • (\w+)捕获函数名,\((.*)\) 捕获括号内全部参数串。
  • 匹配失败说明是无参函数,走 args = []分支。

异常兜底

except Exception as e:
    if isinstance(e, UnicodeEncodeError):
        return match.group(0)
    ...
    return match.group(0)
  • 解析失败时返回原始占位符而不是抛异常。
  • 单个变量写错不会导致整个请求失败,便于定位问题。
  • 对 UnicodeEncodeError 单独判断,是为了兼容 Windows GBK 控制台(打印中文/emoji 会报编码错误)

示例:

# 接口测试的请求体配置
body:
  phone: "${random_phone()}"
  name: "${generate_chinese_name()}"
  sign: "${md5(123456)}"
  ts: "${timestamp()}"

3.接口测试:

代码如下:apps/api_testing/request_utils.py

"""
接口测试公共工具模块。

集中此前散落在 views.py / utils.py 中重复拷贝的逻辑:
- 环境变量 {{var}} 替换(字符串 / 递归 dict)
- 动态函数 ${func()} 解析(委托 core.VariableResolver)
- 环境变量合并(GLOBAL + LOCAL,局部覆盖全局)
- 请求组装(headers / params / body,支持 json / raw / form-urlencoded / form-data)
- 响应提取(extractors:json_path / header / status_code / regex)→ 供接口关联使用
"""
import json
import re

from .variable_resolver import VariableResolver


# ---------------------------------------------------------------------------
# 1. 环境变量 {{var}} 替换
# ---------------------------------------------------------------------------
def replace_env_variables(text, variables):
    """把文本中的 {{key}} 占位符替换为环境变量值。

    变量值支持两种形态:普通标量,或前端变量对象 {currentValue, initialValue}。
    """
    if not isinstance(text, str):
        return text
    result = text
    for key, value in (variables or {}).items():
        if isinstance(value, dict):
            replacement = str(value.get('currentValue', '') or value.get('initialValue', ''))
        else:
            replacement = str(value) if value is not None else ''
        result = result.replace(f'{{{{{key}}}}}', replacement)
    return result


def replace_env_variables_in_obj(data, variables):
    """递归替换 dict / list / str 中的 {{var}}。"""
    if isinstance(data, dict):
        return {k: replace_env_variables_in_obj(v, variables) for k, v in data.items()}
    if isinstance(data, list):
        return [replace_env_variables_in_obj(item, variables) for item in data]
    if isinstance(data, str):
        return replace_env_variables(data, variables)
    return data


def resolve_dynamic_in_obj(data, resolver):
    """递归解析 dict / list / str 中的 ${func()} 动态函数。"""
    if isinstance(data, dict):
        return {k: resolve_dynamic_in_obj(v, resolver) for k, v in data.items()}
    if isinstance(data, list):
        return [resolve_dynamic_in_obj(item, resolver) for item in data]
    if isinstance(data, str):
        return resolver.resolve(data)
    return data

代码说明:

变量:

语法语义数据来源
{{key}}环境变量静态替换环境配置表(全局+局部)
${func()}动态函数运行时生成数据工厂工具
  • 分开的好处:环境变量是"配置"(如 {{base_url}}、{{token}}),动态函数是"生成"(如 ${random_phone()}),语义清晰、便于排查。如果混用一种语法,出问题时分不清是配置错了还是函数错了。

递归处理嵌套结构

def replace_env_variables_in_obj(data, variables):
    if isinstance(data, dict):
        return {k: replace_env_variables_in_obj(v, variables) for k, v in data.items()}
    if isinstance(data, list):
        return [replace_env_variables_in_obj(item, variables) for item in data]
    if isinstance(data, str):
        return replace_env_variables(data, variables)
    return data
  • 请求体是多层嵌套的 JSON(如 `{"data": {"user": {"phone": "{{phone}}"}}}`),
      必须递归才能替换到最内层。
  • 三个分支覆盖 dict/ list / str,最后 return data 兜底数字、布尔等类型。
  • 递归遍历(对树形结构做递归映射):按节点类型分派,容器节点递归处理子节点,叶子节点就地处理,碰到非容器类型即终止。写法短且不会漏层级。

兼容两种变量值形态:

if isinstance(value, dict):
    replacement = str(value.get('currentValue', '') or value.get('initialValue', ''))
else:
    replacement = str(value) if value is not None else ''
  • 前端传来的变量可能是**普通标量**,也可能是对象{currentValue, initialValue}(用于界面展示当前值与原值)。
  • 用 or 短路:currentValue 为空时回退到 initialValue。

4.断言执行引擎

代码如下:apps/api_testing/utils.py

def execute_assertions(response, assertions):
    """执行断言验证"""
    results = []

    for assertion in assertions:
        result = {
            'name': assertion.get('name', '未命名断言'),
            'type': assertion.get('type'),
            'passed': False,
            'expected': assertion.get('expected'),
            'actual': None,
            'error': None
        }

        try:
            assertion_type = assertion.get('type')
            expected = assertion.get('expected')
            actual = None
            passed = False

            if assertion_type == 'status_code':
                actual = response.status_code
                passed = actual == expected

            elif assertion_type == 'response_time':
                # 响应时间断言在调用方处理
                actual = assertion.get('actual_time')
                passed = actual <= expected if actual else False

            elif assertion_type == 'contains':
                text = response.text or ''
                pattern = str(expected)
                actual = text[:200] + '...' if len(text) > 200 else text
                passed = pattern in str(text)

            elif assertion_type == 'json_path':
                json_path = assertion.get('json_path', '')
                expected_value = assertion.get('expected')
                actual = None
                passed = False

                try:
                    # 检查响应是否为JSON格式
                    content_type = response.headers.get('content-type', '').lower()
                    if 'application/json' not in content_type:
                        raise ValueError(f"响应不是JSON格式,Content-Type: {content_type}")

                    response_json = json.loads(response.text)

                    # 检查JSONPath表达式是否为空
                    if not json_path:
                        raise ValueError("JSON路径表达式不能为空")

                    from jsonpath_ng import parse
                    matches = parse(json_path).find(response_json)
                    actual = matches[0].value if matches else None
                    passed = str(actual) == str(expected_value)

                    # 确保actual值被正确设置到result中
                    result['actual'] = actual
                except json.JSONDecodeError as e:
                    actual = None

代码说明

统一的断言结果结构

result = {
    'name': ..., 'type': ..., 'passed': False,
    'expected': ..., 'actual': None, 'error': None
}
  • 每条断言都产出结构一致的结果对象,这样前端可以直接渲染成表格(断言名/预期/实际/是否通过),不需要为每种断言类型写不同的展示逻辑。

六种断言类型:

type界面显示名校验内容实现要点
status_code状态码HTTP状态码直接比较response.status_code
response_time响应时间响应时间是否超时耗时由调用方测量后传入
contains包含文本响应文本是否含子串阶段到200字符避免报告过长
json_pathJSON路径按JSONPath取值比较用jsonpath_ng解析
header响应头指定响应头的值response.headers.get(header_name)
equals完全匹配响应全文与预期完全相等response.text.strip() == str(expected).strip()

json_path比equals更常用。

5.JWT双Token+前端无感刷新

后端:Token策略配置(backend/settings.py)

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),   # access_token 60分钟
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7),      # refresh_token 7天
    'ROTATE_REFRESH_TOKENS': True,      # 刷新时轮换 refresh_token
    'BLACKLIST_AFTER_ROTATION': True,   # 旧的 refresh_token 加入黑名单
    'UPDATE_LAST_LOGIN': True,          # 更新最后登录时间

    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'AUTH_HEADER_TYPES': ('Bearer',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
}

登录接口签发双Token(apps/users/views.py)

@api_view(['POST'])
@permission_classes([permissions.AllowAny])
@csrf_exempt
def login_view(request):
    serializer = LoginSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    user = serializer.validated_data['user']

    # JWT Token (优先使用JWT)
    refresh = RefreshToken.for_user(user)
    access_token = str(refresh.access_token)
    refresh_token = str(refresh)

    return Response({
        'user': UserSerializer(user).data,
        'access': access_token,       # JWT access token
        'refresh': refresh_token,     # JWT refresh token
        'message': '登录成功'
    })

代码说明:

双token

Access TokenRefresh Token
有效期60分钟7天
携带频率每次业务请求都带只在刷新时用

防重放:

'ROTATE_REFRESH_TOKENS': True,      # 刷新时签发新 refresh
'BLACKLIST_AFTER_ROTATION': True,   # 旧 refresh 立即进黑名单
  • 每次刷新都换一对新 Token,旧的 refresh 立刻作废
  • 这样即使 refresh 被窃取,攻击者用过一次后原用户下次刷新就会失败,从而暴露异常

前端:请求排队无感刷新(frontend/src/utils/api.js)

// 正在刷新的标志
let isRefreshing = false
// 等待刷新的请求队列
let failedQueue = []

// 处理队列中的请求
const processQueue = (error, token = null) => {
  failedQueue.forEach(prom => {
    if (error) {
      prom.reject(error)
    } else {
      prom.resolve(token)
    }
  })
  failedQueue = []
}

// 请求拦截器
api.interceptors.request.use(
  async (config) => {
    const userStore = useUserStore()

    // 检查是否是刷新token的请求
    if (config.url === '/auth/token/refresh/') {
      return config
    }

    // 如果有access token
    if (userStore.accessToken) {
      // 检查token是否即将过期(5分钟内)
      if (userStore.isTokenExpiringSoon && !userStore.isTokenExpired) {
        // 如果没有正在刷新,开始刷新
        if (!isRefreshing) {
          isRefreshing = true
          try {
            const newToken = await userStore.refreshAccessToken()
            processQueue(null, newToken)
            // 更新当前请求的token
            config.headers.Authorization = `Bearer ${newToken}`
          } catch (error) {
            processQueue(error, null)
            return Promise.reject(error)
          } finally {
            isRefreshing = false
          }
        } else {
          // 如果正在刷新,将请求加入队列
          return new Promise((resolve, reject) => {
            failedQueue.push({ resolve, reject })
          }).then(token => {
            config.headers.Authorization = `Bearer ${token}`
            return config
          })
        }
      }

      config.headers.Authorization = `Bearer ${userStore.accessToken}`
    }

    return config
  },
  (error) => Promise.reject(error)
)

用“标志位+队列”把并发的刷新请求串行化,保证同一时刻只有一个刷新

let isRefreshing = false   // 是否正在刷新
let failedQueue = []       // 等待刷新的请求队列
变量作用
isRefreshing保证同一时刻只有一个刷新
failedQueue挂起等待中的请求,刷新完统一唤醒

6.AI模型统一接入层

代码如下:apps/requirement_analysis/models.py

class AIModelConfig(models.Model):
    """AI模型配置"""
    ROLE_CHOICES = [
        ('writer', '测试用例编写专家'),
        ('reviewer', '测试评审专家'),
        ('browser_use_text', 'Browser Use - 文本模式'),
        ('browser_use_vision', 'Browser Use - 视觉模式'),
    ]

    name = models.CharField(max_length=100, verbose_name='配置名称')
    model_type = models.CharField(max_length=50, choices=MODEL_TYPE_CHOICES,
                                  verbose_name='模型类型')
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, verbose_name='角色')
    api_key = models.CharField(max_length=200, verbose_name='API Key', blank=True, null=True)
    base_url = models.URLField(verbose_name='API Base URL')
    model_name = models.CharField(max_length=100, verbose_name='模型名称')
    max_tokens = models.IntegerField(default=4096, verbose_name='最大Token数')
    temperature = models.FloatField(default=0.7, verbose_name='温度')
    is_active = models.BooleanField(default=True, verbose_name='是否启用')

    @classmethod
    def get_active_config(cls, model_type: str, role: str):
        return cls.objects.filter(
            model_type=model_type,
            role=role,
            is_active=True,
        ).first()

base_url归一化管理

base_url = config.base_url.rstrip('/')

if not base_url.endswith('/chat/completions'):
    version_match = re.search(r'/v(\d+)/?$', base_url)
    if version_match:
        url = f"{base_url}/chat/completions"
    else:
        # 但对于某些API(如DeepSeek),base_url可能已经是 https://api.deepseek.com
        url = f"{base_url}/v1/chat/completions"
else:
    url = base_url

角色路由:按用途分类

ROLE_CHOICES = [
    ('writer', '测试用例编写专家'),
    ('reviewer', '测试评审专家'),
    ...
]

API Key 的脱敏处理(apps/requirement_analysis/serializers.py)

class AIModelConfigSerializer(serializers.ModelSerializer):
    api_key_masked = serializers.SerializerMethodField(read_only=True)

    class Meta:
        fields = ['id', 'name', 'model_type', 'role', 'api_key', 'api_key_masked',
                  'base_url', 'model_name', ...]
        extra_kwargs = {
            'api_key': {'write_only': True}  # API Key只用于写入,不在响应中返回
        }

    def get_api_key_masked(self, obj):
        if obj.api_key:
            if len(obj.api_key) > 7:
                return f"{obj.api_key[:3]}{'*' * (len(obj.api_key) - 7)}{obj.api_key[-4:]}"
            return '*' * len(obj.api_key)

AI-TestHub:基于大模型与规则引擎的测试用例自动生成平台 在软件研发流程中,接口测试用例的设计与维护长期依赖人工经验,存在覆盖不全、边界值遗漏、接口变更后存量用例失效等痛点。大语言模型(LLM)的成熟为自动化生成测试用例提供了新的技术路径,但直接依赖模型输出又面临格式不稳定、字段编造等问题。AI-TestHub平台提出一种“LLM生成+规则引擎校验+模板渲染”的工程化方案,通过结构化字段清单、覆盖矩阵规划、语义检索召回相似用例,实现从接口文档到多维测试用例的自动生成。平台利用Prompt工程与少样本示范提升输出质量,并通过校验重试机制保障格式合法率与逻辑自洽性,最 阅读详情

相关推荐

UI自动化测试实战:从Selenium到Playwright的鼠标键盘操作精讲

UI自动化测试是现代软件工程中保障产品质量的关键技术,其核心原理是通过程序模拟用户在界面上的交互行为,实现测试流程的自动化执行。在Web端自动化测试中,鼠标和键盘操作是模拟用户意图的物理载体,精准稳定的操作模拟是构建可靠自动化脚本的基石。从技术价值角度看,自动化测试不仅能提升测试效率,减少重复劳动,更能实现快速回归验证,为持续集成和敏捷开发提供支撑。在应用场景上,无论是电商下单流程、后台管理系统操作,还是富文本编辑器交互,都离不开对点击、输入、拖拽等基础操作的精准控制。本文聚焦于**Selenium**和*

weixin_30721077的博客 321

AI-TestHub:我如何从零开发一个智能测试用例生成平台

这个项目是我对“AI + 测试”方向的一次探索尝试。起初的想法很朴素:既然 AI 能理解文档、能生成代码,那能否帮助测试同学从重复的用例编写中解放出来?带着这个问题,我开始从 0 到 1 搭建这个平台。在技术实现上,两阶段 Prompt 的设计是反复调试后的成果——先让 AI 提取需求点,再基于需求点生成用例,将复杂任务拆解为多个简单任务的组合,生成的用例质量显著提升。异步任务加轮询的模式则解决了 AI 调用长耗时与用户体验之间的矛盾,后台独立执行,前端实时反馈,让等待变得可感知。

black_snack的博客 2006

Kylin-Desktop-V10-SP1-General-Release-2303-X86_64-海光版(hygon c86)安装与配置指南

本文提供了在国产海光C86处理器平台上安装与配置Kylin Desktop V10 SP1操作系统的详细指南。内容涵盖从下载专用镜像、制作启动盘、BIOS关键设置、磁盘分区方案,到系统安装后的驱动优化、必备软件安装及常见故障排除,旨在帮助用户顺利完成hygon c86平台的系统部署与优化。

tree的博客 1378

TestHubo - 一款开源免费的一站式测试管理工具

推荐一款开源免费的全栈测试平台 - TestHubo ,与仅关注单一领域的工具(如 Postman、Selenium)不同,TestHubo 提供一站式测试解决方案,涵盖功能测试、接口测试、性能测试以及 Web 和 App 测试等多个维度。TestHubo 整合了全面的测试能力,使团队可以在一个平台内完成所有测试需求。。

tiklab2021的博客 1676

Windows事件ID 10010报错:DCOM注册超时与Game Bar修复指南

DCOM(分布式组件对象模型)是Windows中用于跨进程组件通信的核心机制,其注册超时问题常表现为事件ID 10010报错。当Game Bar的PresenceWriter组件因权限配置不当、组件损坏或开机资源竞争而无法在限定时间内向DCOM注册时,系统会记录该错误,并可能伴随DCOM服务主机CPU占用升高或游戏栏功能异常。理解DCOM激活原理与Network Service账户权限,有助于从权限修复、组件重注册及开机时序优化等层面系统排查。本文针对不同Windows版本,提供从静默处理到根治的完整方案,

weixin_33563728的博客 935

华为云HCS解决方案笔记HUAWEI CLOUD Stack【面试篇】

华为云解决方案介绍、华为云服务介绍,华为云网络平面规划等

weixin_48375618的博客 1万+

如何借助AI工具辅助部署TestHub 智能测试管理平台

开启智能终端新体验,OrcaTerm AI,重塑终端交互范式,集自然语言交互、上下文感知与智能释义于一体,我们为终端注入了前所未有的强大核心,让它真正听懂、看懂、并为你服务。通过这个工具连接服务器后,可以像跟AI对话一样的来操作服务器,然后testhub项目的部署步骤在项目的文档中都有 ,直接跟AI对话,他就可以帮我一步一步的安装好。:DeepSeek、通义千问、硅基流动、OpenAI、Anthropic、Google Gemini 等主流模型一键配置。项目已经部署的差不多了,接下来就是慢慢的调试功能了。

专注测试领域知识分享和技能提升 4117

论文学习| PoseDiffusion: 通过扩散辅助的束调整解决姿态估计问题

解决的问题:相机姿态估计,这是计算机视觉中的一个经典问题。提出的方法:一种新的方法,PoseDiffusion,它将Structure from Motion(SfM,从运动中构建结构)问题整合到概率扩散框架中。优势模拟捆绑调整的迭代过程。整合来自极线几何的几何约束。在具有挑战性的场景中(如视角稀疏、基线宽的情况)表现良好。能够为多张图片预测内外参。结果:在现实世界数据集上,PoseDiffusion超越了经典的SfM管道和学习型方法,在不同数据集上具有跨数据集的泛化能力,无需进一步训练。

麻辣小香郭的博客 1990

探索TestHub:一款高效、灵活的测试管理工具

在软件开发过程中,测试是不可或缺的一环。良好的测试管理能够确保产品质量,提高团队协作效率。今天,我们向您推荐一个开源项目——TestHub(),它是一个专为现代敏捷开发团队打造的测试用例管理和自动化测试平台。 ## 项目简介 TestHub提供了一个直观且易于使用的界面,帮助开发者和测试人员组织和跟踪他们的测试工作。无论是手动测试用例还是自动化脚本,都能在这个平台上进行有效管理。其核心功能包括...

gitblog_00100的博客 1001

别怕,“卷积”其实很简单

首先给大家讲一个关于卷积的小故事:小明是杭州某互联网大厂的员工,每天996十分辛苦,但小明最近爱上了打台球,经常不在工位。这天,小明的主管让小明改一个需求,小明却到楼下找产品部小丽打台球去了,被主管发现,他非常气愤,扇了小明一巴掌(注意,这就是输入信号,脉冲)。于是小明脸上会渐渐地(贱贱地)鼓起来一个包,小明的脸就是一个系统,而鼓起来的包就是小明的脸对巴掌的响应。好,这样就和信号系统建立起来意义对应的联系。

Machine Learning with Tutors 3181

SRAM ECC原理与应用:从汉明码到高可靠嵌入式系统设计

在嵌入式系统与高可靠性计算领域,内存数据完整性是系统稳定运行的基石。软错误(Soft Error)由宇宙射线或芯片内部粒子引发,可能导致存储单元发生位翻转,进而引发程序跑飞或数据损坏。为应对这一物理层挑战,错误校验与纠正(ECC)技术应运而生,其核心原理基于扩展汉明码,通过增加校验位实现单错误纠正、双错误检测(SECDED)能力。这项技术通过硬件透明纠错,在提升系统可靠性的同时,几乎不影响访问性能。在汽车电子、工业控制及航空航天等对安全性要求极高的场景中,ECC已成为保障长期稳定运行的关键硬件机制。本文聚焦

weixin_28728265的博客 532

AI 测试平台:怎么做的更好?

我自己实测:付费顶配的 Testhub,主要是打通数据链路,但测试还是需要自己手动通过元素定位(UI测试),接口测试(通过 Google 浏览器导入 network HAR 文件,导入不全),还是不能满足提升效率的需求,跟不上 AI 开发的进度。从测试设计缓慢、手工且不一致、测试逻辑沉淀在个人脑中、无可追溯、无迭代闭环、模板工具 ≠ 真实测试覆盖、AI生成更快更多 ≠ 质量证据链,:市面上 AI 辅助测试,都是在【点】上做事,有的生成用例、有的造测试数据、有的写接口脚本等,但拼不到一起。

Debroon 862

Cadence Orcad Capture原理图断线的检查与避免方法

在设计电路图中,尤其是直接借鉴别人的设计进行二次开发的过程中难免会遇到如下尴尬的情况。有没有眼神好的朋友发现图纸的哪里有不对劲的地方呢?

fydar的博客 3269

强力指南:用TestHub彻底改变你的接口自动化测试工作流

你是否曾为接口测试的碎片化管理而烦恼?当API数量激增、测试用例散落在各个脚本文件中,团队协作变得举步维艰。TestHub正是为解决这一痛点而生的接口自动化测试管理平台,它将零散的测试用例整合为统一的管理体系,让持续集成测试变得前所未有的高效。 ## 价值主张:从混乱到有序的测试革命 在敏捷开发时代,接口测试往往面临三大挑战:测试用例难以复用、测试结果无法统一分析、团队协作缺乏标准化流程。Te

gitblog_00205的博客 885

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

TestHub|基于 Django+Vue3 的 AI 驱动全栈智能测试管理平台

AI 需求文档解析:上传 PDF/Word/TXT 需求文档,自动提取业务需求点,一键生成测试用例,减少人工编写成本。Browser‑use AI 智能 Web 自动化:不需要写脚本,输入自然语言测试任务,AI 自动解析页面 DOM,自主完成页面操作、断言,支持文本模式 / 视觉模式。Dify 智能助手:内置测试领域 AI 助手,支持会话聊天,解答测试疑问。多模型统一配置。

Wu28242085491的博客 336

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

全栈测试平台 TestHub

TestHub 是一个AI驱动的全栈测试管理平台,融合自动化、代码化与平台化演进,实现测试全流程智能化。它基于Django+Vue3架构,集成接口、UI、APP自动化及50+数据工具,核心创新在于将AI嵌入需求分析与用例生成,推动测试从“脚本执行”向“智能协作”跃迁。平台通过可视化操作降低门槛,以统一资产管理和定时调度提升效率,真正实现质量保障的高效闭环。

qq_46221455的博客 133

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

TestHub-AI测试管理平台

本文介绍基于Django+DRF+Vue3构建的AI驱动测试管理平台,通过整合需求分析、用例生成、自动化执行等模块,实现“需求→用例”从8小时压缩至30分钟。核心亮点包括:AI需求分析模块采用三层防御机制保障大模型输出结构化可用;多模型统一接入层支持OpenAI兼容协议,灵活切换角色与提供商;APP自动化采用设备资源池与状态机管理,解决并发冲突。结合Celery异步调度与Channels实时推送,平台实现高可靠、可扩展的测试全流程管理,验证了工程化落地对AI能力转化的关键作用。

2202_75689104的博客 267

3个理由为什么TestHub能提升你的团队测试效率50%

在当今快速迭代的软件开发环境中,质量保障团队面临前所未有的挑战:传统手动测试耗时费力,自动化测试框架集成复杂,测试报告可读性差,团队协作效率低下。这些问题不仅拖慢了产品交付速度,还增加了项目风险。TestHub作为一款开源的接口自动化测试一体化解决方案,正是为解决这些痛点而生。 TestHub通过整合Java+TestNG+Maven+Jenkins+ExtentReports+Retrofit

gitblog_00220的博客 500

2026年AI测试工具选型指南:开源平台与商业方案的深度对比

在2026年的AI测试工具选型中,开源阵营展现出了极强的生命力。如果团队的核心诉求是全链路覆盖(需求+接口+UI)、高度的自主可控性以及灵活的扩展性,WHartTest 和 TestHub 都是值得关注的方案。WHartTest 通过 MCP 协议和工程化安全机制,在 AI 深度赋能与企业级安全落地方面表现突出;而 TestHub 则凭借 Browser-use 智能模式与双引擎驱动,为团队提供了一站式的自动化测试体验。

sevens__7的博客 528

构建稳定高效的UI自动化测试体系:从Playwright实践到持续交付

UI自动化测试是现代软件持续交付流程中的关键环节,其核心价值在于为快速迭代提供可靠的质量反馈。其基本原理是通过模拟用户操作,对Web应用界面进行端到端的功能验证。要实现这一技术价值,关键在于克服环境依赖、执行缓慢与维护成本高等工程挑战,从而在CI/CD流水线中实现灵活集成。在实际应用场景中,这涉及到测试框架设计、智能等待策略、并行执行与容器化环境治理等一系列工程实践。本文聚焦于如何利用如Playwright这样的现代工具,结合页面对象模型(POM)与data-testid等健壮定位策略,并集成TestHub

weixin_30628801的博客 379

2026年AI UI自动化测试工具推荐

# 2026年AI UI自动化测试工具推荐 现在选 AI UI 自动化测试工具,已经不只是"用 Selenium 还是 Appium"的问题了。团队真正头疼的是:用例创建效率上不去、脚本维护成本随版本迭代滚雪球、失败定位靠人肉翻日志、多人协作时环境对不齐。尤其在金融、电商这类高频发版的业务里,UI 每变一次,回归套件就可能大面积失效。本文整理 2026 年最值得关注的 8 款主流 AI UI 自

2501_92406411的博客 440

智能测试用例生成平台:从一句话需求到结构化用例的AI工程实践

软件测试中,测试用例的编写往往是最耗时且依赖个人经验的环节。大模型技术的兴起为测试用例的自动化生成提供了全新思路,但要真正落地为可用工具,关键在于如何将自然语言需求转化为结构化、可执行的用例。其核心原理包括测试点拆分、Prompt约束、输出格式解析、去重覆盖度校验以及质量过滤等工程手段,这些环节共同决定生成结果的可用率。在工程实践中,借助FastAPI、异步任务队列、PostgreSQL等成熟技术,可以搭建一套支撑大模型调用的内部平台,将测试人员从重复劳动中解放出来,使其专注于复杂业务判断与异常场景补漏。本

weixin_28839439的博客 218

TestHub:Java接口自动化测试的终极解决方案

TestHub是一个基于Java的接口自动化测试平台,为企业级应用提供完整的测试解决方案。通过集成多种主流技术栈,TestHub让接口自动化测试变得简单高效,是Java测试平台中的佼佼者。 ## 🚀 TestHub快速入门指南 ### 核心架构解析 TestHub采用现代化的技术架构,将接口自动化测试的各个环节完美融合。主要技术组件包括: - **Java** - 核心编程语言 - **

gitblog_00967的博客 1078

TestHub自动化测试平台部署使用

TestHub 部署与使用指南摘要 TestHub是一个测试管理平台,部署过程分为后端和前端两部分。后端使用Python+Django框架,需配置MySQL数据库并初始化数据;前端基于Node.js环境构建。部署完成后,通过两个终端分别启动前后端服务,访问地址分别为localhost:3000和localhost:8000。系统提供API文档和管理后台,支持创建超级用户、修改密码等常用操作。部署时需注意环境变量配置和依赖安装,启动后可通过Ctrl+C停止服务。

2301_79941274的博客 1642

模型之外的那套装置:用 AI harness 完成一个三端同步应用

AI harness 指模型之外、让模型能持续产出可交付软件的全部工程装置。本文把它称作"支架"。它由五层构成:输入层规定模型读什么,反馈层规定模型怎么知道自己错了,观测层把界面与设备状态变成可读事实,记忆层让跨会话不失真,边界层规定哪些动作不许模型自行作出。支架的价值在六天里可以直接计量。

Leo的Matrix 167
上一篇: 力扣面试题二Python
阿蔹
博客等级 码龄3年 67粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值