Inngest 端到端测试实战指南:本地 Dev Server 构建、测试过滤与 Connect Gateway 调试
【免费下载链接】inngestThe leading workflow orchestration platform. Run stateful step functions and AI workflows on serverless, servers, or the edge.项目地址: https://gitcode.com/GitHub_Trending/in/inngest
本指南基于 Inngest 仓库根目录下的 TESTING.md,系统讲解如何用本地代码构建 Dev Server、启动 JS SDK 并运行 Go 端到端测试套件,同时覆盖测试过滤、环境变量语义与 Connect Gateway 详细日志的开启方法。读完本文,你将掌握 Inngest 完整的本地 E2E 测试工作流,并能从源码层面理解测试基础设施的运作原理。
一、测试体系总览
Inngest 的测试体系分为两层:
- 单元测试:直接运行在 Go 包内部的
go test ./pkg/...等,验证各模块的独立行为; - 端到端测试(E2E):位于仓库根目录的
tests/目录下,需要完整的运行环境——一个本地 Dev Server 实例、一个真实运行的 SDK 应用,以及测试进程三者协同工作。
TESTING.md 中描述的三步流程正是 E2E 测试的标准启动路径,也是本指南的核心内容。
三步流程概览
- 用本地代码构建并启动 Dev Server;
- 启动 JS SDK(Next.js 应用)作为被测函数宿主;
- 通过环境变量将测试进程指向上述两个服务,运行
go test测试套件。
二、第一步:用本地构建启动 Dev Server
基本命令
go run ./cmd dev --no-discovery这是 TESTING.md 给出的第一步命令。与直接使用npx inngest-cli@latest dev(安装预编译 CLI)不同,go run ./cmd dev会编译当前仓库的源码并运行,确保你测试的是本地改动后的行为——这正是贡献者验证自己代码修改的首选方式。
--no-discovery的作用
--no-discovery用于禁用应用自动发现。默认情况下,Dev Server 会自动扫描并连接本地运行的应用(例如开发中的 Next.js 应用);但在测试场景中,应用地址由SDK_URL环境变量显式指定,自动发现反而可能引入不确定性。
从 cmd/devserver/cmd.go 的源码可以看到:
&cli.BoolFlag{ Name: "no-discovery", Usage: "Disable app auto-discovery", },而 cmd/devserver/devserver.go 将其映射为Autodiscover: !noDiscovery,最终传入devserver.StartOpts。也就是说,--no-discovery等价于将自动发现开关置为关闭。
Dev Server 常用启动参数
结合 cmd/devserver/cmd.go 中的 CLI 定义,测试场景中常用的参数如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
--port/-p | 8288(见 pkg/api/api.go) | Dev Server 的 API 端口,测试中的API_URL需与此一致 |
--host | 空 | Inngest 服务监听地址 |
--no-discovery | false | 关闭应用自动发现 |
--no-poll | false | 关闭对应用的轮询更新 |
--poll-interval | 5秒(pkg/devserver/devserver.go) | 应用更新的轮询间隔 |
--queue-workers | 100(pkg/devserver/devserver.go) | 从队列执行步骤的执行器 worker 数 |
--tick | 150毫秒(pkg/devserver/devserver.go) | 执行器轮询队列的间隔 |
--persist | false | 重启之间持久化数据 |
--log-level/-l | info | 日志级别:trace、debug、info、warn、error(cmd/root.go) |
例如,指定日志级别与端口运行:
go run ./cmd dev --no-discovery --log-level debug -p 8288启动健康检查
仓库根目录的 tests.sh 脚本展示了官方如何等待 Dev Server 就绪——通过轮询健康检查端点:
while [ $attempt -lt $max_attempts ]; do if curl -s -f http://127.0.0.1:8288/health > /dev/null 2>&1; then echo "Dev server started successfully" break fi ... done这意味着你可以用curl http://127.0.0.1:8288/health快速验证 Dev Server 是否已启动成功,再继续后续步骤。
三、第二步:启动 JS SDK
基本命令
cd tests/js && yarn dev注意:TESTING.md 使用
yarn dev;仓库根目录的 tests.sh 使用pnpm install --frozen-lockfile与pnpm dev。请根据你本地的包管理器与 tests/js/package.json 的实际脚本选择其一。
tests/js是一个 Next.js 应用(参见 tests/js/next.config.js),其作用是为 E2E 测试提供真实的 SDK 函数宿主。测试套件通过SDK_URL环境变量指向它,Dev Server 则通过该地址调用 SDK 暴露的函数。
默认情况下,SDK 开发服务器监听http://127.0.0.1:3000/api/inngest,这与下文测试命令中的SDK_URL=http://127.0.0.1:3000/api/inngest一一对应。
四、第三步:运行 E2E 测试
标准测试命令
INNGEST_SIGNING_KEY=test API_URL=http://127.0.0.1:8288 SDK_URL=http://127.0.0.1:3000/api/inngest go test ./tests -v -count=1环境变量语义
测试入口 tests/main.go 定义了以下环境变量:
| 环境变量 | 含义 | 示例值 |
|---|---|---|
SDK_URL | SDK 应用的地址(/api/inngest端点) | http://127.0.0.1:3000/api/inngest |
API_URL | Dev Server / Inngest API 地址 | http://127.0.0.1:8288 |
EVENT_URL | 事件发送地址,未设置时默认取API_URL | http://127.0.0.1:8288 |
INNGEST_SIGNING_KEY | 函数注册与鉴权使用的签名密钥 | test(开发环境值) |
INNGEST_EVENT_KEY | 发送事件所用的 Event Key,未设置时默认eventkey | eventkey |
PROXY_URL | 测试代理服务器前缀,默认http://localhost | http://localhost |
其中EVENT_URL的逻辑在 tests/main.go 中体现:当未显式设置时自动回退到API_URL,因为 Dev Server 同时承载事件 API 与核心 API。
命令参数解析
go test ./tests:运行仓库根目录下tests/包中的全部 E2E 测试;-v:输出每个测试的详细执行过程;-count=1:禁用 Go 测试缓存,确保每次都真实执行。这对 E2E 测试至关重要——它们依赖外部服务状态,缓存结果毫无意义。
测试运行机制
tests/main.go中的run()函数(tests/main.go)揭示了 E2E 测试的内部机制:
- 在随机端口(40000~50000)启动一个测试代理服务器,拦截执行器与 SDK 之间的所有 HTTP 请求;
- 通过
introspect()调用 SDK 的/api/introspect端点,确认被测函数存在; - 将函数注册到 Dev Server(
/fn/register),并把步骤的 URL 重写为代理地址; - 测试过程中断言代理捕获的请求/响应是否符合预期;
- 测试结束后通过
/fn/remove注销应用。
这种方式让测试既能真实驱动执行器-SDK 交互,又能精确断言消息内容。
五、过滤测试:只跑你关心的用例
使用-test.run过滤
INNGEST_SIGNING_KEY=test API_URL=http://127.0.0.1:8288 SDK_URL=http://127.0.0.1:3000/api/inngest go test ./tests -v -count=1 -test.run TestSDKCancelNotReceived-test.run接受正则表达式,匹配测试函数的名称。上面这条命令只运行TestSDKCancelNotReceived,该测试位于 tests/sdk_cancel_test.go,验证取消事件未收到时 step 串行执行(step.run→step.sleep→step.run)的行为,Timeout 为 20 秒。
过滤技巧
- 精确匹配单个用例:
-test.run '^TestSDKCancelNotReceived$'; - 匹配一组用例:
-test.run 'TestStep'会运行所有名称包含TestStep的测试; - 配合子测试:
-test.run 'TestSDKCancelNotReceived/Without_a_cancellation_event'可进一步过滤到某个t.Run子测试(见 tests/sdk_cancel_test.go)。
官方脚本的过滤封装
仓库根目录的 tests.sh 支持透传测试模式:
./tests.sh 'TestSDKCancelNotReceived'脚本会先安装依赖、启动 JS SDK、等待 Dev Server 健康检查通过,再以-run "$TEST_PATTERN"运行./tests包;之后还会运行./tests/golang下的 Go SDK 测试(例如 tests/golang/connect_test.go 中的TestEndToEnd,覆盖 Connect Gateway 的长连接工作流)。
六、Connect Gateway 详细日志
背景:为什么默认看不到 Connect 日志
Inngest 的 Connect Gateway 是处理 SDK 长连接(WebSocket)的组件。在 Dev Server 模式下,为了避免日志噪音,默认会抑制 Connect Gateway 的详细日志。
这一行为在 pkg/connect/service.go 中实现:
func (c *connectGatewaySvc) Pre(ctx context.Context) error { // Set up gateway-specific logger with info for correlations c.logger = logger.StdlibLogger(ctx).With("gateway_id", c.gatewayId) if c.dev { // Hide verbose connect gateway logs in dev server by default if os.Getenv("CONNECT_GATEWAY_FULL_LOGS") != "true" { c.logger = logger.VoidLogger() } } ... }可以看到:当处于 dev 模式(c.dev为 true)且环境变量CONNECT_GATEWAY_FULL_LOGS不等于"true"时,日志器被替换为logger.VoidLogger(),即所有日志被丢弃。
开启详细日志
CONNECT_GATEWAY_FULL_LOGS=true go run ./cmd dev --no-discovery --log-level trace与普通启动相比,这里有两个关键点:
CONNECT_GATEWAY_FULL_LOGS=true:环境变量,让 Connect Gateway 恢复日志输出;--log-level trace:将全局日志级别提升到trace,输出最详尽的信息。
TESTING.md 明确指出这是在需要调试 Connect Gateway 时的做法,例如排查 SDK 连接建立、心跳、租约续期(lease)、pause 消息等长连接问题。
日志中的关联字段
开启后,Connect Gateway 的日志会带上关联信息,方便在多实例场景下追踪。从 pkg/connect/service.go 可以看到:
c.logger = c.logger.With( "gateway_group", c.groupName, "gateway_hostname", c.hostname, "gateway_ip", c.ipAddress.String(), )即每条日志会包含gateway_id、gateway_group、gateway_hostname、gateway_ip等字段,可用于跨日志行串联分析某个网关实例的行为。
七、一条命令跑完:tests.sh 脚本
如果想跳过手工三步流程,可以直接使用仓库根目录的 tests.sh 自动化脚本。它的执行流程是:
- 清理占用端口
8288的残留进程; - 安装
tests/js依赖并后台启动 JS SDK(pnpm dev); - 后台启动
go run ./cmd dev --no-discovery,将输出重定向到dev-stdout.txt/dev-stderr.txt; - 轮询
http://127.0.0.1:8288/health等待 Dev Server 就绪(最多 10 次、每次间隔 1 秒); - 运行
./tests的 Go E2E 测试(支持传入-run模式过滤); - 运行
./tests/golang的 Go SDK 测试; - 捕获
SIGINT/SIGTERM后清理全部后台进程。
使用方式:
# 全部测试 ./tests.sh # 仅运行指定测试 ./tests.sh 'TestSDKCancelNotReceived'八、常见问题排查
1. 端口被占用或测试串扰
多个测试会话同时运行会争用8288、3000端口。参考 tests.sh 的做法,启动前先清理占用进程,或改用不同端口(--port配合API_URL同步修改)。
2. 测试结果被缓存
E2E 测试依赖外部服务状态,务必始终携带-count=1,否则 Go 可能直接返回上一次的缓存结果,造成“测试通过但实际未执行”的假象。
3. 函数注册失败或事件未触发
检查INNGEST_SIGNING_KEY是否与 Dev Server 期望一致(开发环境常为test),以及SDK_URL是否准确指向 SDK 的/api/inngest端点。测试进程在init()中解析这些变量(tests/main.go),变量缺失或 URL 无 host 会直接 panic。
4. Connect Gateway 行为异常但无日志
先确认是否处于 dev 模式且未设置CONNECT_GATEWAY_FULL_LOGS=true——此时日志被VoidLogger丢弃(pkg/connect/service.go),并非程序没有输出。另外注意--log-level的全局级别也需足够低(如trace)才能看到详细内容。
总结
Inngest 的 E2E 测试链路清晰地划分为“本地 Dev Server(cmd/devserver/cmd.go)→ JS SDK 宿主(tests/js)→ Go 测试进程(tests/main.go)”三个部分,由API_URL、SDK_URL、INNGEST_SIGNING_KEY等环境变量串联。掌握-test.run过滤、-count=1防缓存,以及CONNECT_GATEWAY_FULL_LOGS与--log-level trace的调试组合,你就能高效地验证本地代码改动、精准定位失败用例,并深入排查 Connect Gateway 的长连接问题。若需全自动执行,仓库根目录的 tests.sh 脚本已封装完整的编排流程。
【免费下载链接】inngestThe leading workflow orchestration platform. Run stateful step functions and AI workflows on serverless, servers, or the edge.项目地址: https://gitcode.com/GitHub_Trending/in/inngest
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考