PicoClaw 集成测试体系实战:从 Docker 套件到 Go Integration Tag 的完整落地指南
2026/9/20 22:57:50 网站建设 项目流程
  • 人工智能
  • AI 应用
  • AI Agent
  • 交互助手
  • 工具调用
  • MCP Clients
  • Agent 记忆

【免费下载链接】picoclaw

Tiny, Fast, and Deployable anywhere — automate the mundane, unleash your creativity

项目地址:https://gitcode.com/gh_mirrors/pi/picoclaw
点击查看免费下载

导读

本文围绕 PicoClaw 仓库中的 integration/README.md 展开,系统讲解该项目"两层式"集成测试体系:Go 侧以//go:build integration构建标签守护的*_integration_test.go测试实现,配合 Docker Compose 启动真实依赖的套件(suite)封装。你将掌握 CI 合并前到底执行了什么、运行脚本 的内部工作原理、以mcp-streamable为参照的套件目录结构,以及如何按七步流程为一次真实回归场景新增一个可自动发现、可复现、可清理的集成测试套件。


一、为什么需要独立于单元测试的集成测试层

单元测试擅长验证"函数体内"的纯逻辑,但当多个 PR 各自修改相邻代码路径、问题只在各组件被拼装起来后才暴露时,单元测试很容易漏掉。PicoClaw 的集成测试正是为了抓住这类回归而存在,典型场景包括:

  • 跨包边界的协议兼容性:例如 MCP 客户端与服务器之间的 JSON-RPC 消息编排;
  • 真实传输上的请求/响应行为:HTTP、SSE、stdio、streamable 流式等传输语义;
  • 子进程、CLI 或容器的装配:依赖外部二进制、容器服务的启动与连接;
  • 通过环境变量传递的配置:配置项如何在进程边界间传播;
  • 涉及多组件的启动、发现与拆除流程:从连上服务器、列出工具、调用工具到关闭清理的完整生命周期。

这些场景的共同特征是"风险存在于交互之中",这正是 integration/README.md 明确定义的集成测试边界。

二、两层集成测试机制:Go 测试实现与 Docker 套件封装

PicoClaw 当前使用两套相关联的机制,README 特别强调了两者的分工:

层次载体职责
Go 集成测试*_integration_test.go,文件首行//go:build integration测试实现本身,以构建标签显式隔离
Docker 套件integration/suites/ 下的suite.env+docker-compose*.yml让测试可复现、CI 安全的运行方式

一句话概括:tagged Go 测试是"测什么",Docker 套件是"怎么跑得稳"

需要说明的是,并非所有带integration标签的测试都进入了 Docker 套件。例如 pkg/providers/cli/ 下的真实 CLI 冒烟测试依赖本机已安装的外部二进制,属于**主动选择(opt-in)**的手工验证手段,不参与 PR 合并门禁。

三、CI 到底跑什么:脚本、工作流与自动发现

按 README 的说明,.github/workflows/pr.yml.github/workflows/build.yml中的集成测试任务都执行同一条命令:

bash ./scripts/run-integration-tests.sh

这个设计的关键在于自动发现:runner 脚本会遍历 integration/suites/ 下的每一个目录,因此新增一个套件不需要改动 GitHub Actions 工作流文件——把目录放进去,CI 就能感知到它。这也解释了为什么 README 强调:任何想真正保护"PR 之间的合并"的测试,都必须能从 scripts/run-integration-tests.sh 触达。

3.1 Makefile 入口

仓库根目录的 Makefile 提供了统一入口:

.PHONY: all build install uninstall clean help test integration-test build-all lint-docs integration-test: @bash ./scripts/run-integration-tests.sh

make integration-test与直接执行脚本等价。

四、Runner 工作原理:脚本级剖析

scripts/run-integration-tests.sh 是整个体系的调度中枢,其核心流程如下:

  1. 校验基础文件:检查integration/docker-compose.runner.ymlintegration/suites目录是否存在,缺失直接报错退出。
  2. 收集套件目录:未传参数时用find "$SUITES_DIR" -mindepth 1 -maxdepth 1 -type d | sort全量发现;传入参数(如mcp-streamable)则按给定名称收集,便于只跑单个套件。
  3. 构造 compose 参数:对每个套件生成独立的 Docker Compose 项目名picoclaw-int-<套件名>(由sanitize_project_name统一转为小写、非字母数字替换为-),并叠加加载基础 compose 文件与套件内所有docker-compose.yml/docker-compose.*.yml(按文件名排序)。
  4. 加载清单:在子 shell 中以set -a+source "$manifest"方式导入suite.env,同时导出INTEGRATION_REPO_ROOT指向仓库根目录(供 compose 的build.context使用)。
  5. 强制校验TEST_COMMAND未定义则直接报错退出;RUNNER_SERVICE缺省时取默认值integration-runner
  6. 启动依赖服务:用docker compose config --services列出全部服务,剔除 runner 自身后,对依赖服务执行docker compose up -d --build --wait,即构建并等待服务就绪(依赖 healthcheck)。
  7. 执行测试命令docker compose run --rm "$runner_service" "$TEST_COMMAND",将TEST_COMMAND作为单个参数传给以bash -c为 entrypoint 的 runner 容器执行。
  8. 自动清理:通过trap cleanup EXIT保证无论成功失败都执行docker compose down -v --remove-orphans,清掉容器、命名卷与孤儿资源。

脚本本身也有针对性的单元测试:integration/run_integration_tests_script_test.go 会在临时套件目录中写入一个suite.envTEST_COMMAND='printf runner-ok')和一个含fake-dependency服务的 compose 文件,再用 stubdocker二进制替换真实 docker 记录参数,验证脚本确实把套件命令执行了起来。

4.1 共享 runner 容器的环境约定

integration/docker-compose.runner.yml 定义了所有套件共享的 runner 服务:

services: integration-runner: image: golang:1.25-bookworm working_dir: /workspace entrypoint: ["bash", "-c"] volumes: - ${INTEGRATION_REPO_ROOT}:/workspace - picoclaw-integration-gocache:/go-build-cache - picoclaw-integration-gomodcache:/go-mod-cache environment: GOCACHE: /go-build-cache GOMODCACHE: /go-mod-cache GOTOOLCHAIN: local CGO_ENABLED: "0" GOFLAGS: -tags=goolm,stdjson,integration

其中最关键的一行是GOFLAGS: -tags=goolm,stdjson,integration——它保证套件内运行的所有测试与 CI 使用完全相同的构建标签,避免"本机通过、CI 因标签不同而行为不一致"的经典陷阱。同时通过命名卷复用 Go 构建与模块缓存(GOCACHEGOMODCACHE),显著缩短多套件连续运行的时间;GOTOOLCHAIN: local锁定本机工具链,CGO_ENABLED: "0"禁用 CGO 以保证镜像内可构建。

五、参考套件 mcp-streamable 深度拆解

integration/suites/mcp-streamable/ 是当前仓库的参考实现,它同时示范了"fixture 服务器 + 环境变量注入 + 真实服务连接测试"的完整范式。

5.1 套件清单与编排文件

suite.env指定要执行的 Go 测试:

TEST_COMMAND='go test ./pkg/mcp -run TestIntegration_RealConfiguredServer -v'

docker-compose.yml 完成两件事:覆盖 runner 的环境变量注入,以及定义真实依赖服务:

services: integration-runner: depends_on: mcp-streamable-server: condition: service_healthy environment: PICOCLAW_MCP_REAL_SERVER_JSON: >- {"enabled":true,"type":"http","url":"http://mcp-streamable-server:8080/mcp"} PICOCLAW_MCP_REAL_TOOL_NAME: echo PICOCLAW_MCP_REAL_TOOL_ARGS_JSON: >- {"message":"hello from docker integration suite"} PICOCLAW_MCP_REAL_EXPECT_SUBSTRING: hello from docker integration suite mcp-streamable-server: build: context: ${INTEGRATION_REPO_ROOT} dockerfile: integration/fixtures/mcp-streamable-server/Dockerfile environment: STREAMABLE_JSON_RESPONSE: "true"

两点工程细节值得注意:

  • 用 Docker 服务名代替硬编码端口:runner 通过http://mcp-streamable-server:8080/mcp访问依赖服务,而不是127.0.0.1:8080,这符合 README"prefer Docker service names over hard-coded host ports"的建议;
  • 依赖就绪控制depends_on: condition: service_healthy与 runner 脚本的up -d --build --wait配合,确保健康检查通过后才启动测试。

5.2 fixture 服务器:最小可复现的 MCP 服务端

fixture 位于 integration/fixtures/mcp-streamable-server/。main.go 基于官方github.com/modelcontextprotocol/go-sdk/mcp构建了一个最小 streamable MCP 服务器:

  • 注册名为picoclaw-integration-streamable-server的实现,并挂载一个echo工具:接收message参数,原样返回为文本内容;
  • 通过mcp.NewStreamableHTTPHandler/mcp挂载到 HTTP mux,支持以STREAMABLE_JSON_RESPONSE环境变量(默认true)切换 JSON 响应模式;
  • 额外提供/healthz健康检查端点,直接返回ok,供 Dockerfile 的 HEALTHCHECK 探活。

配套 Dockerfile 采用多阶段构建:golang:1.25-bookworm阶段构建二进制,alpine:3.22阶段以非 root 用户appuser运行,并声明:

HEALTHCHECK --interval=5s --timeout=3s --retries=12 CMD wget -qO- http://127.0.0.1:8080/healthz || exit 1

这与 runner 的--wait配合,成为依赖服务"就绪判定"的机制来源。

5.3 两个互补的集成测试:进程内兼容性与真实服务装配

  • pkg/mcp/manager_real_server_integration_test.go 中的TestIntegration_RealConfiguredServer真实服务装配路径。它从环境变量PICOCLAW_MCP_REAL_SERVER_JSON读取服务器配置,经NewManager().ConnectServer连接、GetAllTools断言至少发现一个工具,再通过CallTool调用指定工具并断言返回文本包含PICOCLAW_MCP_REAL_EXPECT_SUBSTRING。测试还支持PICOCLAW_MCP_REAL_EXPECT_TOOL_COUNT精确校验工具数量,并预留了 stdio 子进程服务器(如npx启动的 filesystem server)的配置示例。对应 Docker 套件注入的环境变量即:真实服务器地址http://mcp-streamable-server:8080/mcp、调用echo工具、参数{"message":"hello from docker integration suite"}、期望子串hello from docker integration suite——一个完整闭环。
  • pkg/mcp/manager_integration_test.go 中的TestIntegration_StreamableHTTPCompatibility协议行为路径。它用httptest.NewServer在进程内起一个可记录请求的服务端,覆盖三种组合(http+ JSON-only 响应、http+ 流式响应、streamable-http别名 + JSON-only),并借助requestRecorder验证协议级细节:streamable 模式不允许出现独立的 GET 请求、必须存在带Mcp-Session-Id的 POST、结束时恰好一次带会话 ID 的 DELETE、所有请求都携带Authorization: Bearer integration-token,且initialize/tools/list/tools/call三类 JSON-RPC 方法的响应 Content-Type 与预期一致(application/jsontext/event-stream)。

如 README 所述,两个测试覆盖同一区域但互补:一个在进程内验证协议行为,一个验证真实服务的装配与数据通路,共同构成"协议 + 服务接线"的双保险。

六、套件布局与必需清单字段

任何新增套件目录必须包含两类文件,缺一不可:

integration/suites/my-suite/ ├── docker-compose.yml └── suite.env

suite.env由 runner 脚本 source,必须定义:

字段是否必需说明
TEST_COMMAND必需在集成 runner 容器内执行的 shell 命令,通常是一条go test
RUNNER_SERVICE可选覆盖默认 runner 服务名integration-runner

最小示例:

TEST_COMMAND='go test ./pkg/mcp -run TestIntegration_RealConfiguredServer -v'

七、本地运行集成测试

7.1 前置条件

  • Docker +docker compose插件:Docker 套件必需;
  • Go 1.25+:仅当你选择在本机直接跑 tagged 集成测试、而非通过 Docker 时才有需要(以 docker-compose.runner.yml 中golang:1.25-bookworm镜像所对应的工具链版本为准)。

7.2 三种运行粒度

运行 CI 跑的全部套件:

make integration-test

等价命令:

bash ./scripts/run-integration-tests.sh

只跑单个套件(复现 CI 中该套件的完整路径最快的方式):

bash ./scripts/run-integration-tests.sh mcp-streamable

直接跑 tagged Go 测试(编写测试时迭代最快,免去 Docker 开销):

go test -tags=goolm,stdjson,integration ./pkg/mcp -run TestIntegration_StreamableHTTPCompatibility -v

也可以手动注入与 Docker 套件相同的环境变量,直接运行真实服务器冒烟测试:

PICOCLAW_MCP_REAL_SERVER_JSON='{"enabled":true,"type":"http","url":"http://127.0.0.1:8080/mcp"}' \ PICOCLAW_MCP_REAL_TOOL_NAME=echo \ PICOCLAW_MCP_REAL_TOOL_ARGS_JSON='{"message":"hello"}' \ PICOCLAW_MCP_REAL_EXPECT_SUBSTRING=hello \ go test -tags=goolm,stdjson,integration ./pkg/mcp -run TestIntegration_RealConfiguredServer -v

两点注意事项(README 明确强调):

  • 避免-short:当前集成测试在 short 模式下会跳过(两个 MCP 集成测试均在入口处t.Skip("skipping integration test in short mode"));
  • 先用go test做紧反馈循环,提交前再用bash ./scripts/run-integration-tests.sh <suite-name>验证 Docker 套件端到端可用。

八、何时该添加集成测试

判断标准是风险是否在交互之中。适合上集成测试的场景:

  • 跨越进程或容器边界的代码;
  • 传输特定行为:HTTP、SSE、stdio 或 streamable MCP 流;
  • 依赖真实子进程执行的 CLI 解析与装配;
  • 配置经文件、环境变量、请求头或服务发现传播的链路;
  • 多个本身合理的 PR 合并后才显现的回归。

行为是纯函数、完全可控于进程内时,优先写单元测试。

九、添加新集成测试的七步流程

第 1 步:从你想防住的回归出发

先写清楚"合并后可能崩掉的真实工作流",场景越尖锐,测试越经得起时间。例如:

  • "传输归一化改动后,PicoClaw 仍能连上 streamable MCP 服务器。"
  • "响应处理重构后,provider 包装层仍能解析真实 CLI 的输出格式。"

第 2 步:实现 Go 测试

在所属包内新增或扩展*_integration_test.go,文件首行加构建标签:

//go:build integration

编写指南:

  • 断言聚焦可观察行为
  • 使用有界的超时(参考示例中的30*time.Second/10*time.Second);
  • 优先用确定性 fixture,避免依赖公网或共享外部状态;
  • 仅当依赖是刻意可选的(如本机安装的第三方 CLI)才使用 skip。

第 3 步:决定它是否要卡 CI 合并

经验法则:

  • 只是手工冒烟检查 → tagged Go 测试可能就够;
  • 要防止回归通过 PR 合并落地 →必须接进 Docker 套件

第 4 步:复用或新增套件

已有套件覆盖同一子系统则直接扩展;否则新建:

integration/suites/<name>/ ├── docker-compose.yml └── suite.env

可复用的 helper 服务或假服务器放进 integration/fixtures/。

第 5 步:定义套件命令

suite.env中用TEST_COMMAND指向要跑的 Go 测试:

TEST_COMMAND='go test ./pkg/mcp -run TestIntegration_RealConfiguredServer -v'
TEST_COMMAND='go test ./pkg/somepkg -run TestIntegration_MyScenario -v'

多个测试共享同一环境时可在一条命令中跑,但套件要保持内聚、便于失败时定位。

第 6 步:在 Docker Compose 中建模依赖

套件 compose 文件可以:定义测试所需的依赖服务、扩展或覆盖共享的integration-runner、向 runner 注入测试消费的环境变量。实操建议:

  • 用 Docker 服务名而不是硬编码主机端口;
  • 依赖服务就绪依赖时加 healthcheck;
  • 让套件自包含、确定性。

第 7 步:提交前本地验证

go test -tags=goolm,stdjson,integration ./path/to/package -run TestIntegration_Name -v bash ./scripts/run-integration-tests.sh <suite-name>

前者服务于编写期,后者证明 CI 路径端到端可用。

十、新套件审查清单

开 PR 前逐项核对,新套件应当:

  • 复现一个真实的多组件失败模式
  • 确定性与隔离性良好;
  • CI 中无需手工准备
  • 失败输出清晰
  • 运行时间合理;
  • 通过常规 runner teardown自我清理

提交后,套件会被 CI 集成任务自动发现,无需改动任何工作流文件——这是这套体系"以目录即配置"的最终优势。


补充说明:本文涉及的版本与标签(如goolmstdjson、Go 1.25)均以当前仓库 integration/docker-compose.runner.yml、Makefile 与 scripts/run-integration-tests.sh 中的实际内容为准;若需要验证任意一步,均可直接在仓库内对照上述路径的源码与测试执行。

  • 人工智能
  • AI 应用
  • AI Agent
  • 交互助手
  • 工具调用
  • MCP Clients
  • Agent 记忆

【免费下载链接】picoclaw

Tiny, Fast, and Deployable anywhere — automate the mundane, unleash your creativity

项目地址:https://gitcode.com/gh_mirrors/pi/picoclaw
点击查看免费下载
上一篇:PhoneInfoga 多源数据聚合:把 5 个数据源并排比着验证一个号码
下一篇:【限时免费】 《zinx的安装与使用教程》

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询