- 人工智能
- AI 应用
- AI Agent
- 交互助手
- 工具调用
- MCP Clients
- Agent 记忆
【免费下载链接】picoclaw
Tiny, Fast, and Deployable anywhere — automate the mundane, unleash your creativity
导读
本文围绕 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 是整个体系的调度中枢,其核心流程如下:
- 校验基础文件:检查
integration/docker-compose.runner.yml与integration/suites目录是否存在,缺失直接报错退出。 - 收集套件目录:未传参数时用
find "$SUITES_DIR" -mindepth 1 -maxdepth 1 -type d | sort全量发现;传入参数(如mcp-streamable)则按给定名称收集,便于只跑单个套件。 - 构造 compose 参数:对每个套件生成独立的 Docker Compose 项目名
picoclaw-int-<套件名>(由sanitize_project_name统一转为小写、非字母数字替换为-),并叠加加载基础 compose 文件与套件内所有docker-compose.yml/docker-compose.*.yml(按文件名排序)。 - 加载清单:在子 shell 中以
set -a+source "$manifest"方式导入suite.env,同时导出INTEGRATION_REPO_ROOT指向仓库根目录(供 compose 的build.context使用)。 - 强制校验:
TEST_COMMAND未定义则直接报错退出;RUNNER_SERVICE缺省时取默认值integration-runner。 - 启动依赖服务:用
docker compose config --services列出全部服务,剔除 runner 自身后,对依赖服务执行docker compose up -d --build --wait,即构建并等待服务就绪(依赖 healthcheck)。 - 执行测试命令:
docker compose run --rm "$runner_service" "$TEST_COMMAND",将TEST_COMMAND作为单个参数传给以bash -c为 entrypoint 的 runner 容器执行。 - 自动清理:通过
trap cleanup EXIT保证无论成功失败都执行docker compose down -v --remove-orphans,清掉容器、命名卷与孤儿资源。
脚本本身也有针对性的单元测试:integration/run_integration_tests_script_test.go 会在临时套件目录中写入一个suite.env(TEST_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 构建与模块缓存(GOCACHE、GOMODCACHE),显著缩短多套件连续运行的时间;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/json或text/event-stream)。
如 README 所述,两个测试覆盖同一区域但互补:一个在进程内验证协议行为,一个验证真实服务的装配与数据通路,共同构成"协议 + 服务接线"的双保险。
六、套件布局与必需清单字段
任何新增套件目录必须包含两类文件,缺一不可:
integration/suites/my-suite/ ├── docker-compose.yml └── suite.envsuite.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 集成任务自动发现,无需改动任何工作流文件——这是这套体系"以目录即配置"的最终优势。
补充说明:本文涉及的版本与标签(如
goolm、stdjson、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
相关推荐
Onyx(danswer)Coda 连接器集成测试套件实战指南:从环境准备到 CI/CD 落地
Onyx(danswer)Coda 连接器集成测试套件实战指南:从环境准备到 CI/CD 落地 本篇指南聚焦开源 AI 平台 Onyx(原 danswer)中
AI 应用大模型RAGAI Agent后端前端Chainlink 本地 CRE 系统测试实战:从 smoke 套件到 CI 维护的完整指南
Chainlink 本地 CRE 系统测试实战:从 smoke 套件到 CI 维护的完整指南 本指南围绕 Chainlink 仓库中 Local CRE(本地链
区块链Web3后端如何用Fasttracker 2 Clone创作专业电子音乐:完整入门指南
如何用Fasttracker 2 Clone创作专业电子音乐:完整入门指南 想要制作复古风格的电子音乐但不知从何开始?Fasttracker 2 Clone是你
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考