1. 项目概述:当ComfyUI工作流遇上自动化测试

如果你和我一样,是个深度依赖ComfyUI进行AI图像生成与编辑的创作者或开发者,那你一定经历过这种痛苦:辛辛苦苦搭建了一个复杂的工作流,里面集成了Qwen-Image-Edit-F2P这样的图像编辑大模型节点,跑了几次效果惊艳。但某天,你更新了一个自定义节点,或者ComfyUI本体升级了版本,再跑这个工作流时,要么直接报错,要么生成的图片效果天差地别。你不得不手动重新测试每一个节点、每一个参数,这个过程枯燥、耗时且极易遗漏。这正是回归测试要解决的问题——确保软件修改后,原有功能依然正常。

“Qwen-Image-Edit-F2P自动化测试框架”这个项目,就是瞄准了这个痛点。它的核心目标,是 为基于ComfyUI和Qwen-Image-Edit-F2P模型构建的特定图像编辑工作流,打造一套用Python脚本驱动的、可重复执行的自动化回归测试体系 。简单说,就是把“手动点按钮、看结果”的测试过程,变成一段可以随时运行、自动比对结果的代码。这不仅仅是“偷懒”,更是项目工程化、保证交付质量的关键一步。无论是你个人维护的复杂工作流,还是团队协作开发的ComfyUI自定义节点包,这套框架都能帮你建立起可靠的质量防线。

这个框架的价值,对于不同角色的从业者来说各有侧重:

  • 对于AI工作流开发者 :它能确保你发布的工作流模板或节点在不同环境下稳定运行,提升用户信任度。
  • 对于算法研究员 :在迭代Qwen-Image-Edit-F2P模型的参数或前后处理逻辑时,可以快速验证改动是否影响了核心编辑能力(如局部重绘、风格迁移)。
  • 对于技术爱好者 :这是学习如何将AI应用(ComfyUI)与软件工程最佳实践(自动化测试)结合的优秀实践案例。

接下来,我将为你彻底拆解如何从零搭建这样一个框架,分享我在实践中趟过的坑和积累的经验。

1.1 核心需求与挑战解析

在动手之前,我们必须明确要自动化测试的对象是什么。一个典型的“Qwen-Image-Edit-F2P”工作流,其核心测试需求通常包括:

  1. 功能正确性 :给定一张输入图片和一个编辑指令(如“将天空替换为星空”),工作流是否能正确调用Qwen-Image-Edit-F2P节点并输出一张编辑后的图片?这是最基础的测试。
  2. 输出一致性(回归) :在ComfyUI版本、节点版本、Python依赖库不变的情况下,同一组输入(种子固定)是否每次都能产生像素级完全相同的输出?这是防止“静默损坏”的关键。
  3. 输出质量稳定性 :即使随机种子不同,生成的图片在关键指标(如美学评分、与指令的语义一致性)上是否保持在一个可接受的波动范围内?
  4. 异常处理 :当输入非法(如图片损坏、指令为空)或资源不足(显存溢出)时,工作流是否有合理的错误处理或提示,而不是直接崩溃?
  5. 性能基准 :完成一次推理的平均耗时、显存占用是多少?这有助于评估工作流在不同硬件上的可行性。

然而,在ComfyUI环境下实现这些测试,面临几个独特挑战:

  • 非标准API接口 :ComfyUI主要通过WebSocket或HTTP API与前端通信,其工作流执行是一个异步过程,不像普通Python库那样直接函数调用。
  • 状态与资源管理 :测试需要管理ComfyUI服务器的启动/停止、模型加载、GPU内存清理,过程比纯代码测试复杂。
  • 结果评估困难 :图像生成的结果是主观的,自动化判断“图片好不好”或“编辑得对不对”非常困难,需要结合多种方法。

因此,我们的自动化测试框架不能只是一个简单的 unittest 套件,而是一个需要处理服务通信、任务队列、结果收集和复杂断言的系统工程。

2. 框架整体设计与技术选型

基于上述需求,一个健壮的自动化测试框架应该包含以下几个核心模块,我将其设计为下图所示的架构。这个架构清晰地展示了从测试用例定义到最终报告生成的完整数据流和控制流。

flowchart TD
    A[测试用例定义<br>(JSON/API描述)] --> B[测试执行引擎<br>(Python脚本核心)]
    
    B --> C{通信控制层}
    C --> D[ComfyUI Server<br>(待测目标)]
    
    D --> E[结果输出<br>(图像/数据)]
    E --> F[验证与断言模块]
    
    F --> G[测试报告生成<br>(HTML/JSON)]
    
    subgraph B [执行引擎核心组件]
        B1[测试调度器]
        B2[工作流组装器]
        B3[异步任务管理器]
    end
    
    subgraph C [通信控制层]
        C1[WebSocket Client]
        C2[HTTP API Client]
    end
    
    subgraph F [验证模块]
        F1[像素比对]
        F2[特征相似度 SSIM/PSNR]
        F3[美学评分预测]
        F4[CLIP指令一致性]
    end

技术栈选型与理由:

  1. 核心语言:Python 3.8+

    • 理由 :ComfyUI本身基于Python,生态兼容性最好。拥有最丰富的测试库( pytest , unittest )、图像处理库( PIL , opencv-python )和AI相关库( torch , transformers )。社区资源庞大,遇到问题容易找到解决方案。
  2. 测试运行器:Pytest

    • 理由 :相比标准的 unittest pytest 的 fixture 机制非常适合管理ComfyUI服务器、API客户端等测试资源。其断言写法更直观,插件生态丰富(如并行测试 pytest-xdist 、报告生成 pytest-html ),能极大提升测试编写和执行的体验。
  3. ComfyUI 通信:WebSocket + 官方 comfy 工具包

    • 理由 :ComfyUI的工作流执行和进度反馈天然通过WebSocket进行。直接使用Python的 websockets 库进行底层通信是可行的,但更推荐使用社区封装好的工具,例如通过 comfy 的Python API(通常位于ComfyUI安装目录的 comfy 文件夹内)或第三方库如 comfy-cli 。它们封装了连接、工作流提交、图片获取等细节,让我们的测试脚本更专注于业务逻辑。
    • 备选 :对于简单的触发执行,也可以使用HTTP POST API( /prompt 接口),但无法方便地获取实时进度。
  4. 图像处理与比对:OpenCV-Python 和 Pillow

    • 理由 PIL (Pillow)适合基础的图片加载、格式转换和保存。 OpenCV 则提供了强大的图像相似度计算功能,如PSNR(峰值信噪比)、SSIM(结构相似性指数),这是进行像素级回归测试的基石。
  5. 高级结果验证(可选但推荐):

    • CLIP模型 :用于计算生成图片与文本指令的语义相似度,评估“图文相关性”。可以使用 transformers 库加载轻量化的CLIP模型(如 openai/clip-vit-base-patch32 )。
    • 美学评分模型 :如 AVA 数据集上训练的模型,用于自动化评估生成图片的美学质量。这属于进阶需求,初期可以不做。
  6. 测试数据管理:JSON + 文件系统

    • 理由 :测试用例(输入图路径、编辑指令、预期输出特征)可以用JSON文件定义。测试生成的图片、日志文件需要有一个清晰的目录结构来管理,例如:
      tests/
      ├── data/
      │   ├── input_images/
      │   ├── expected_outputs/ (可选,存放基准图)
      │   └── test_cases.json
      ├── results/
      │   ├── runs/
      │   │   └── 20240501_120000/ (每次测试运行一个文件夹)
      │   │       ├── outputs/
      │   │       ├── logs/
      │   │       └── report.html
      │   └── latest -> runs/20240501_120000 (软链接)
      └── conftest.py
      

注意 :在选型时,务必考虑你的ComfyUI部署方式。如果是本地便携包,可以直接引用其Python环境中的模块。如果是远程服务器,则需要确保网络连通性,并且可能需要对模型加载等环节做特殊处理(例如确保服务器已预加载Qwen-Image-Edit-F2P模型)。

3. 环境搭建与核心模块实现

3.1 基础环境与依赖安装

首先,我们需要一个独立的Python虚拟环境来管理测试框架的依赖,避免与ComfyUI的环境冲突。

# 1. 创建并激活虚拟环境
python -m venv venv_comfyui_test
# Windows:
venv_comfyui_test\Scripts\activate
# Linux/Mac:
source venv_comfyui_test/bin/activate

# 2. 安装核心测试与图像处理库
pip install pytest pytest-html pytest-xdist
pip install opencv-python pillow
pip install websocket-client # 用于连接ComfyUI WebSocket
# 如果需要CLIP评估
pip install transformers torch

接下来是关键一步:让测试脚本能够与ComfyUI交互。这里有两种主流方式:

方式一:嵌入模式(推荐用于本地测试) 如果你的ComfyUI是以源码形式运行的,可以将测试项目作为其子模块,或者直接导入ComfyUI的模块。这需要你将测试框架的代码放在ComfyUI项目目录内或设置PYTHONPATH。

# 在你的测试项目根目录的 conftest.py 或环境设置脚本中
import sys
import os
# 假设你的ComfyUI源码目录在上一级
sys.path.insert(0, os.path.abspath('../ComfyUI'))

这种方式可以直接实例化ComfyUI的执行逻辑,无需启动HTTP服务器,执行速度最快,也最稳定。

方式二:客户端模式(通用,适用于远程服务器) 通过WebSocket或HTTP与一个运行中的ComfyUI服务器通信。这更贴近用户真实使用场景。

# 示例:使用WebSocket客户端连接
import websocket
import json
import uuid

class ComfyUIClient:
    def __init__(self, server_address="127.0.0.1:8188"):
        self.ws_url = f"ws://{server_address}/ws"
        self.client_id = str(uuid.uuid4())
        self.ws = None

    def connect(self):
        """连接到ComfyUI WebSocket服务器"""
        self.ws = websocket.WebSocket()
        self.ws.connect(self.ws_url)
        # 发送客户端标识
        self.ws.send(json.dumps({"type": "connect", "data": {"client_id": self.client_id}}))
        print(f"Connected to ComfyUI server at {self.ws_url}")

    def queue_prompt(self, workflow):
        """提交工作流JSON并执行"""
        prompt_id = str(uuid.uuid4())
        message = {
            "type": "queue_prompt",
            "data": {"prompt": workflow, "client_id": self.client_id, "prompt_id": prompt_id}
        }
        self.ws.send(json.dumps(message))
        return prompt_id

    def wait_for_result(self, prompt_id):
        """等待并获取执行结果(图片)"""
        # 这里需要循环接收消息,直到收到包含该prompt_id执行完成和图片数据的信息
        # 这是一个简化的示例,实际处理需要解析不同的消息类型(executing, progress, executed)
        images = []
        while True:
            out = self.ws.recv()
            if isinstance(out, str):
                message = json.loads(out)
                if message['type'] == 'executed' and message['data']['prompt_id'] == prompt_id:
                    # 从message['data']['output']中提取图片数据
                    # 通常图片数据是base64编码或文件名,需要额外HTTP请求下载
                    images = self._extract_images(message['data']['output'])
                    break
        return images

实操心得 :我强烈建议在项目初期采用 方式一(嵌入模式) 。虽然设置稍麻烦,但它避免了网络不稳定带来的干扰,测试执行速度快,且更容易调试。你可以先基于嵌入模式开发核心测试逻辑,再封装一个客户端适配器,以便未来切换到方式二。

3.2 测试用例的抽象与定义

测试用例不能硬编码在脚本里。我们需要一个结构化的方式来定义“测什么”和“怎么判”。

// tests/data/test_cases.json
[
  {
    "id": "test_local_repaint_sky",
    "name": "局部重绘-替换天空",
    "description": "测试Qwen-Image-Edit-F2P对指定区域(天空)进行指令性重绘的能力",
    "workflow_template": "workflows/qwen_image_edit_f2p_api.json", // 工作流模板文件
    "inputs": {
      "input_image": "data/input_images/landscape_with_sky.jpg",
      "edit_instruction": "Replace the sky with a dramatic sunset with orange and purple clouds.",
      "mask_image": "data/input_masks/sky_mask.png", // 可选,如果工作流需要掩码
      "seed": 123456
    },
    "validation": {
      "type": "regression", // 回归测试,比对像素
      "baseline_image": "data/expected_outputs/landscape_sunset_baseline.png",
      "tolerance": {
        "psnr": 40, // PSNR大于40dB认为通过
        "ssim": 0.98 // SSIM大于0.98认为通过
      }
    },
    "timeout": 300 // 超时时间(秒)
  },
  {
    "id": "test_inpainting_object_removal",
    "name": "局部擦除-移除物体",
    "description": "测试通过指令移除图片中特定物体的能力",
    "workflow_template": "workflows/qwen_inpainting_api.json",
    "inputs": {
      "input_image": "data/input_images/room_with_lamp.jpg",
      "edit_instruction": "Remove the floor lamp and make the area look like a plain wall.",
      "seed": 654321
    },
    "validation": {
      "type": "semantic", // 语义测试,使用CLIP计算图文相似度
      "clip_model": "openai/clip-vit-base-patch32",
      "threshold": 0.25 // CLIP相似度分数需大于0.25
    }
  }
]

对应的Python数据类可以这样定义:

# tests/schemas.py
from dataclasses import dataclass
from typing import Optional, Dict, Any
from pathlib import Path

@dataclass
class TestCase:
    id: str
    name: str
    description: str
    workflow_template: Path  # 工作流模板路径
    inputs: Dict[str, Any]   # 输入参数
    validation: Dict[str, Any] # 验证配置
    timeout: int = 120

    def load_workflow(self) -> Dict:
        """加载并渲染工作流JSON模板"""
        with open(self.workflow_template, 'r', encoding='utf-8') as f:
            workflow = json.load(f)
        # 动态替换工作流中的输入节点值
        # 例如,找到"input_image"节点,将其"image"字段替换为实际图片路径或base64数据
        # 这是一个需要根据你具体工作流结构实现的函数
        rendered_workflow = self._render_workflow_inputs(workflow)
        return rendered_workflow

    def _render_workflow_inputs(self, workflow: Dict) -> Dict:
        # 实现逻辑:遍历workflow,根据self.inputs替换对应节点的值
        # 这里需要你深入了解你的ComfyUI工作流JSON结构
        pass

3.3 核心执行引擎与结果验证器

有了测试用例和通信客户端,我们就可以组装核心的执行引擎了。

# tests/executor.py
import json
import time
from pathlib import Path
import cv2
import numpy as np
from .schemas import TestCase
from .client import ComfyUIClient  # 假设这是封装好的客户端

class TestExecutor:
    def __init__(self, comfyui_client: ComfyUIClient, output_dir: Path):
        self.client = comfyui_client
        self.output_dir = output_dir
        self.output_dir.mkdir(parents=True, exist_ok=True)

    def run_test_case(self, test_case: TestCase) -> Dict:
        """执行单个测试用例"""
        print(f"[INFO] 开始执行测试用例: {test_case.name}")
        start_time = time.time()

        # 1. 准备数据
        workflow_data = test_case.load_workflow()
        # 2. 提交并执行
        prompt_id = self.client.queue_prompt(workflow_data)
        # 3. 等待结果(带超时)
        try:
            output_images = self.client.wait_for_result(prompt_id, timeout=test_case.timeout)
        except TimeoutError as e:
            return {
                "id": test_case.id,
                "status": "timeout",
                "message": str(e),
                "duration": time.time() - start_time
            }

        # 4. 保存输出
        saved_paths = []
        for i, img_data in enumerate(output_images):
            # img_data可能是base64或PIL Image对象,取决于client实现
            save_path = self.output_dir / f"{test_case.id}_output_{i}.png"
            self._save_image(img_data, save_path)
            saved_paths.append(str(save_path))

        # 5. 验证结果
        validation_result = self._validate_output(test_case, saved_paths)

        result = {
            "id": test_case.id,
            "status": "passed" if validation_result["passed"] else "failed",
            "outputs": saved_paths,
            "validation_details": validation_result,
            "duration": time.time() - start_time
        }
        print(f"[INFO] 测试用例 {test_case.name} 完成,状态: {result['status']}")
        return result

    def _validate_output(self, test_case: TestCase, output_image_paths: List[str]) -> Dict:
        """根据测试用例定义的验证规则进行结果验证"""
        val_cfg = test_case.validation
        val_type = val_cfg.get("type", "regression")

        if val_type == "regression":
            # 像素级回归测试
            baseline_path = Path(val_cfg["baseline_image"])
            if not baseline_path.exists():
                return {"passed": False, "error": f"基线图片不存在: {baseline_path}"}

            # 通常取第一张输出图进行比对
            current_img = cv2.imread(output_image_paths[0])
            baseline_img = cv2.imread(str(baseline_path))

            if current_img is None or baseline_img is None:
                return {"passed": False, "error": "图片加载失败"}

            # 确保尺寸一致
            if current_img.shape != baseline_img.shape:
                current_img = cv2.resize(current_img, (baseline_img.shape[1], baseline_img.shape[0]))

            # 计算指标
            psnr = self._calculate_psnr(baseline_img, current_img)
            ssim = self._calculate_ssim(baseline_img, current_img)

            passed = (psnr >= val_cfg["tolerance"]["psnr"]) and (ssim >= val_cfg["tolerance"]["ssim"])
            return {
                "passed": passed,
                "metrics": {"psnr": psnr, "ssim": ssim},
                "threshold": val_cfg["tolerance"]
            }

        elif val_type == "semantic":
            # 语义一致性测试(示例,需集成CLIP)
            # 这里简化处理,实际需要加载CLIP模型进行计算
            instruction = test_case.inputs.get("edit_instruction", "")
            # clip_score = calculate_clip_score(output_image_paths[0], instruction)
            # passed = clip_score > val_cfg.get("threshold", 0.2)
            # return {"passed": passed, "clip_score": clip_score}
            return {"passed": True, "note": "语义验证暂未实现"} # 占位

        else:
            return {"passed": False, "error": f"不支持的验证类型: {val_type}"}

    @staticmethod
    def _calculate_psnr(img1, img2):
        """计算PSNR"""
        mse = np.mean((img1 - img2) ** 2)
        if mse == 0:
            return float('inf')
        max_pixel = 255.0
        psnr = 20 * np.log10(max_pixel / np.sqrt(mse))
        return psnr

    @staticmethod
    def _calculate_ssim(img1, img2):
        """计算SSIM(简化版,实际应用建议使用scikit-image或完整实现)"""
        # 这里是一个非常简化的示例,生产环境应使用成熟的库
        C1 = (0.01 * 255) ** 2
        C2 = (0.03 * 255) ** 2
        # ... 复杂的计算过程
        # 为简化,直接返回一个值,实际应实现完整算法
        return 0.95

4. 整合Pytest与实战测试流程

4.1 使用Pytest Fixture管理资源

Pytest的fixture是管理测试依赖(如ComfyUI客户端、临时目录)的利器。

# tests/conftest.py
import pytest
import tempfile
from pathlib import Path
from .client import ComfyUIClient
from .executor import TestExecutor

@pytest.fixture(scope="session")
def comfyui_server():
    """会话级fixture:启动/连接ComfyUI服务器。
       实际项目中,这里可能是一个子进程启动命令。
    """
    # 方案A:假设ComfyUI已在运行,仅创建客户端
    client = ComfyUIClient("127.0.0.1:8188")
    client.connect()
    yield client
    client.disconnect()

    # 方案B:如果需要自动启动服务器(更复杂但更独立)
    # import subprocess
    # proc = subprocess.Popen(["python", "main.py", "--port", "8188"], cwd="../ComfyUI")
    # time.sleep(10) # 等待服务器启动
    # client = ComfyUIClient("127.0.0.1:8188")
    # client.connect()
    # yield client
    # client.disconnect()
    # proc.terminate()

@pytest.fixture(scope="function")
def test_output_dir():
    """每个测试函数一个独立的临时输出目录"""
    with tempfile.TemporaryDirectory() as tmpdir:
        yield Path(tmpdir)

@pytest.fixture
def test_executor(comfyui_server, test_output_dir):
    """组合客户端和输出目录,生成测试执行器"""
    return TestExecutor(comfyui_server, test_output_dir)

@pytest.fixture(scope="session")
def load_test_cases():
    """加载所有测试用例定义"""
    import json
    with open('tests/data/test_cases.json', 'r', encoding='utf-8') as f:
        cases_data = json.load(f)
    from .schemas import TestCase
    return [TestCase(**data) for data in cases_data]

4.2 编写具体的测试函数

现在,我们可以用非常简洁的方式编写测试了。

# tests/test_qwen_image_edit.py
import pytest

class TestQwenImageEditF2P:
    """Qwen-Image-Edit-F2P 工作流自动化测试集"""

    @pytest.mark.parametrize("test_case", load_test_cases(), ids=lambda tc: tc.id)
    def test_workflow_execution(self, test_executor, test_case):
        """参数化测试:遍历所有测试用例并执行"""
        result = test_executor.run_test_case(test_case)

        # 使用pytest断言
        assert result["status"] == "passed", (
            f"测试用例 '{test_case.name}' 失败。\n"
            f"详情: {result.get('validation_details', {})}"
        )
        # 也可以附加一些自定义信息到报告中
        if "metrics" in result.get("validation_details", {}):
            metrics = result["validation_details"]["metrics"]
            pytest.set_test_metric("psnr", metrics.get("psnr", "N/A"))
            pytest.set_test_metric("ssim", metrics.get("ssim", "N/A"))

    # 也可以为特定场景编写专用测试
    def test_large_image_input(self, test_executor):
        """测试大尺寸图片输入时的稳定性和内存占用"""
        # 构造一个特殊的大图测试用例
        large_case = TestCase(...)
        result = test_executor.run_test_case(large_case)
        assert result["status"] != "timeout", "大图处理超时"
        # 可以在这里检查日志中是否有OOM警告(需要扩展执行器以捕获日志)

4.3 运行测试并生成报告

通过命令行即可运行全套测试,并生成美观的HTML报告。

# 运行所有测试
pytest tests/ -v

# 并行运行测试(如果测试用例独立)
pytest tests/ -n auto

# 运行测试并生成HTML报告
pytest tests/ --html=reports/test_report.html --self-contained-html

# 运行特定标记的测试
pytest tests/ -m "not slow"  # 运行非慢速测试

生成的HTML报告会清晰展示每个测试用例的执行状态、耗时、输出图片(如果配置了附件)以及验证指标,非常适合在CI/CD流水线中查看或存档。

5. 高级技巧与避坑指南

在实际搭建和运行这套框架的过程中,我遇到了不少坑,也总结了一些提升效率的技巧。

5.1 如何高效管理“基线”图片?

像素级回归测试的核心是有一张“正确”的基线图。但AI生成具有随机性,如何确定基线?

  • 技巧1:手动确认+版本控制 :在代码/模型版本稳定时,手动运行一次测试,人工审核生成的图片,确认无误后,将其提交到Git仓库作为基线。任何代码或模型更新后,都需要重新评估基线是否需要更新。
  • 技巧2:使用“黄金种子” :为测试用例固定一个随机种子( seed ),确保在相同环境下每次生成完全相同的图片。这样基线图本身就是确定性的。
  • 技巧3:容忍度动态调整 :对于某些细微的、不影响视觉效果的像素波动(可能由于底层库的微小差异导致),可以适当放宽PSNR/SSIM的容忍阈值,而不是要求完全一致。

5.2 处理ComfyUI的异步性与状态

ComfyUI的执行是异步的,且服务器可能有状态(如缓存模型)。

  • 坑:测试相互干扰 :如果测试A加载了模型,测试B可能会直接使用缓存,影响性能测试的准确性。
  • 解决方案
    1. 测试隔离 :每个测试用例执行前,通过API发送一个 /free 请求(如果ComfyUI支持),或重启测试用的ComfyUI进程(使用 subprocess fixture),确保干净的初始状态。
    2. 使用独立客户端ID :确保每个测试或测试会话使用不同的 client_id ,避免消息混淆。
    3. 充分等待 :在 wait_for_result 函数中,不仅要监听 executed 消息,还要处理 executing progress 消息,并设置合理的超时和重试机制。

5.3 测试的稳定性和可重复性

  • 硬件差异 :在不同GPU上,由于cuDNN或PyTorch的确定性设置,即使种子相同,结果也可能有极小差异。在CI环境中(可能使用CPU或不同型号GPU),需要设置更高的容错度,或者将回归测试限定在拥有固定硬件的专用机器上运行。
  • 依赖锁定 :使用 pip freeze > requirements.txt Poetry Pipenv 严格锁定所有Python包的版本,包括PyTorch、CUDA驱动版本。这是保证可重复性的生命线。
  • 清理临时文件 :测试会产生大量图片,务必使用 tempfile pytest 的fixture在测试后自动清理,防止磁盘被撑满。

5.4 将测试集成到CI/CD流水线

框架的真正威力在于自动化。你可以将其集成到GitHub Actions、GitLab CI或Jenkins中。

  • 示例 GitHub Actions 工作流片段
    name: ComfyUI Workflow Regression Test
    on: [push, pull_request]
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - name: Set up Python
            uses: actions/setup-python@v4
            with:
              python-version: '3.10'
          - name: Install ComfyUI & Dependencies
            run: |
              git clone https://github.com/comfyanonymous/ComfyUI.git
              cd ComfyUI
              pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118
              pip install -r requirements.txt
          - name: Install Test Framework
            run: |
              pip install -r test_requirements.txt
          - name: Run Tests
            run: |
              cd your-test-project
              pytest tests/ --html=report.html --self-contained-html
          - name: Upload Test Report
            uses: actions/upload-artifact@v3
            if: always()
            with:
              name: comfyui-test-report
              path: your-test-project/report.html
    
    这样,每次提交代码都会自动运行测试,确保工作流修改不会破坏现有功能。

5.5 扩展方向:更智能的验证

除了像素比对和基础的CLIP评分,还可以探索:

  • 基于Grounding DINO/SAM的物体检测验证 :对于“移除物体”的测试,可以验证生成图中是否真的没有目标物体了。
  • 人工评审队列 :将不确定的测试结果(如处于容忍度边缘的图片)自动放入一个队列,通过简单的内部网页供团队成员快速评审,将人工判断也流程化。

搭建“Qwen-Image-Edit-F2P自动化测试框架”的过程,本质上是将AI应用开发工程化的过程。它开始可能有点繁琐,但一旦建立起来,就会成为你工作流开发中最坚实的安全网,让你能更自信地迭代和交付。从最简单的单个工作流测试开始,逐步扩展到整个节点库的回归测试,这套方法论会持续为你创造价值。

更多推荐