要测试 Webhook,你需要向服务提供商提供一个可访问的 URL,触发一个真实事件,然后确认你的处理程序(handler)接收了 payload 并做出正确的响应。难点在于,你的应用是接收方,而不是调用方,所以你无法直接点击“发送”并读取响应。本指南将介绍你所需的工具(检测服务、本地隧道、提供商触发器)以及一个可重复的流程,以端到端地断言你的处理程序。
为什么 Webhook 比普通 API 更难测试
对于普通的 API 调用,你可以控制请求。你选择方法、设置 body、发送请求并读取响应。Webhook 则相反。提供商会在其自己的时间安排下向你发送请求,且携带它定义的 payload。这种角色反转带来了四个测试问题。
首先,默认情况下你无法自己触发事件。payment_intent.succeeded 事件只有在 Stripe 决定发生时才会触发,因此你需要提供商的工具来强制触发一个事件。
其次,发送是异步的。事件会在上游操作完成时随时到达,因此你的测试必须捕获传入的请求,而不是阻塞等待返回值。
第三,payload 的格式是由提供商定义的。你的处理程序必须精确解析 GitHub、Stripe 或 Slack 发送的内容。
第四,大多数提供商都会对其请求进行签名。你的接口在信任 body 之前必须验证签名 header,如果测试跳过了这一步,就会隐藏真正的 Bug。如需深入了解,请参阅我们的 Webhook 签名验证指南。
如果你仍在决定 Webhook 是否是适合你集成的模式,“Webhook 对比轮询”以及“Webhook 对比 WebSocket”的文章对比了它们之间的权衡。
Webhook 测试工具箱
你通常会在同一个调试过程中用到四种工具:用于查看原始 payload 的捕获服务、用于连接到你笔记本电脑的隧道、用于触发真实事件的提供商触发器,以及用于验证处理程序的断言工具。
1. 检测与捕获服务
在编写任何处理程序代码之前,将提供商指向一个临时的 URL,并查看它实际发送的内容。这些服务会为你提供一个公开的接口,并实时显示每个请求。
webhook.site 在页面加载的瞬间就会为你提供一个唯一的随机 URL 和电子邮件地址。发送给它的所有内容都会立即显示:完整的 body、headers 和方法。免费的 URL 会在 7 天后过期,且上限为 100 个请求,最大请求大小为 10 MB。付费计划则增加了永久 URL、无限请求以及保留最新 10,000 条请求历史的功能。
Beeceptor 提供了一个免费的 HTTPS 接口,你可以将其用作 Webhook 接收器并实时检测传入的 payload。它还提供 mock 服务器接口,因此你可以通过同一个工具进行捕获和模拟。
Pipedream RequestBin 是另一个广泛使用的请求捕获工具。请查看其当前的文档以获取具体的方案分级,因为捕获服务经常会更改其免费额度限制。
使用这些来回答一个问题:真实的 payload 是什么样的?复制一个样本以备后用,以便日后构建可重复的测试。
2. 本地开发:使用隧道暴露 localhost
提供商无法访问你机器上的 localhost:3000。隧道会创建一个公开的 HTTPS URL,将流量转发到你的本地端口,这样你就可以在编辑器中运行处理程序并进行实时调试。
ngrok 是常用的选择。安装它,进行一次身份验证,然后转发端口。
brew install ngrok # macOS
ngrok config add-authtoken $YOUR_TOKEN
ngrok http 3000 # forwards a public HTTPS URL to localhost:3000
免费的 ngrok 账号会获得一个自动分配的开发域名。自定义域名则需要付费计划。
cloudflared 如果你更倾向于使用 Cloudflare,只需一条命令即可快速运行隧道。
cloudflared tunnel --url http://localhost:3000
将隧道输出的公开 URL 粘贴到提供商的 webhook 设置中,传入的事件就会到达你的本地服务端。若要了解此模式的更详细步骤,请参阅如何使用 webhook 服务测试本地 API。
3. 从提供商触发测试事件
只有向捕获 URL 发送数据时,它才起作用。大多数主流提供商都提供了按需触发测试事件的工具,因此你无需制造真实的支付或推送。
Stripe CLI 是最简洁的例子。stripe listen 命令将实时事件从你的 Stripe 沙箱转发到本地路径,并输出一个你需放入应用配置中的 webhook 签名密钥。
# Forward all events to your local handler:
stripe listen --forward-to localhost:3000/webhooks
# Filter to specific events only:
stripe listen --events payment_intent.succeeded,checkout.session.completed \
--forward-to localhost:3000/webhooks
在监听器运行的情况下,stripe trigger 会触发一个测试事件。请注意,触发操作会创建具有实际底层支持的 API 对象并产生副作用:例如,payment_intent.succeeded 也会触发 payment_intent.created。
stripe trigger payment_intent.succeeded
stripe trigger checkout.session.completed
stripe trigger --help # list every supported event
GitHub 保留了简短的投递历史记录供你重放。进入你的仓库,然后依次点击 “Settings(设置)”、在 “Code and automation” 下的 “Webhooks”。点击 webhook URL,打开 “Recent deliveries(最近投递)” 选项卡,点击一个投递的 GUID,然后点击 “Redeliver(重新投递)”。有两点限制需要注意:你只能重新投递过去 3 天内的记录,并且你需要对该仓库拥有管理员权限。GitHub 不会自动重新投递失败的记录,因此重放是手动进行的。
Slack 的传入 webhook 最容易测试。你只需向 webhook URL 发送一个 POST 请求,并携带 JSON 数据即可发送消息。最简 payload 仅包含一个 text 字段。
curl -X POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX \
-H 'Content-type: application/json' \
-d '{"text":"Hello, world."}'
Slack webhook URL 属于机密信息。Slack 会注销泄露的 URL,因此请不要将其放在客户端代码和公开仓库中。
4. 断言你的处理器
捕获和触发只能证明链路是通的,并不能证明你的处理器是正确的。最后的步骤是发送精心构建的 payload,并检查响应以及副作用。最基本的方法是使用带有代表性 body 的 curl 命令。
curl -X POST http://localhost:3000/webhooks \ -H 'Content-Type: application/json' \ -H 'Stripe-Signature: t=…,v1=…' \ -d '{"id":"evttest","type":"paymentintent.succeeded","data":{"object":{"id":"pi_123","status":"succeeded"}}}'单个 curl 命令对于冒烟测试来说已经足够了。但如果你想在 CI 中运行可重复且带断言的测试,它就显得力不从心了。而这正是专用 API 工具的用武之地。
使用 Apifox 测试 Webhook
Apifox 是一款集 API 设计、调试、测试、mock 和文档于一体的全功能 API 平台。它虽然没有专门的“webhook 收件箱”,但本节将实事求是地介绍它所擅长的事情:构建 payload、保存 payload、断言响应以及通过 mock 服务端来替代第三方服务商。
构建并保存示例 payload 作为可复用的请求
将你从 webhook.site 捕获(或从服务商文档中复制)的 payload 构建为 Apifox 请求。将请求方法设置为 POST,粘贴 JSON body,并添加服务商发送的 headers,包括你的处理器需要校验的签名 header。
将该请求保存到对应的接口。现在,你就有了一种可重复、版本受控的方式来向处理器 POST 一个已知正常的(或故意构造错误的)payload,而无需每次都重新输入 curl 命令。
使用可视化断言生成器断言响应
发送 payload 只是测试的一半,另一半则是检查你的处理器返回了什么。在请求或场景步骤中,打开后置操作,点击“+ 添加”,然后选择“断言”。Apifox 将添加一个无需代码即可配置的校验规则。
将断言指向响应 body,并使用 $ 代表 JSON 根节点。例如,定位到 $.data.status,将条件设置为“等于”,并与 succeeded 进行比对。运行请求后,结果将显示在“断言”标签页中。可视化断言是将值作为字符串进行比较的。当你需要进行类型精确的检查(如实数、布尔值)时,可以切换到使用兼容 Postman 的 pm.test 语法的自定义脚本。
这就把“返回了 200”变成了“返回了 200,且 status 字段为 succeeded,且 order id 匹配”。你可以将多个断言构建到一个场景中,以覆盖成功路径、缺失字段的用例以及签名错误的拒绝情况。
使用 mock 服务端来替代第三方服务商
有时你可能想测试相反的方向:你技术栈中的某个服务需要调用第三方服务商。在本地测试期间,你可能根本不想请求真实的服务商。此时,Apifox 的 mock 服务端就可以用来替代它。
Apifox 提供了三种 mock 类型。本地 mock 随桌面客户端运行,且仅在客户端打开时有效。云端 mock 托管在 Apifox 服务端,24/7 全天候运行,支持开启或关闭(默认关闭)。Runner mock 运行在自托管的 runner 基础设施上,并在你的团队中共享。每个 HTTP 接口都有一个 mock 模块;可在文档模式下的 API 标签页或调试模式下的 mock 标签页中复制 mock URL。只有以 / 开头的路径才会路由到 mock 环境。在测试期间,将你的代码指向该 mock URL,你的集成即可获得可预测的服务商响应,而无需进行真实的 API 调用或产生副作用。
使用 Apifox CLI 在 CI 中运行 webhook 测试
一旦你的测试场景在本地通过,就可以在每次推送时使用 Apifox CLI 运行它。这需要 Node.js v16 或更高版本。
npm install -g apifox-cli node -v && apifox -v && which node && which npm && which apifox # verify install
在线运行已保存的测试场景,并传入测试场景 CI/CD“命令行”标签页中的访问令牌和相关 ID。
apifox run --access-token $APIFOXACCESSTOKEN -t 637132 -e 358171 -d 3497013 -r html,cli 这里 -t 表示测试场景,-e 表示环境(必填),-d 表示测试数据(CSV 或 JSON 文件路径,或者已存储的数据集 ID),-r 设置报告格式(支持 cli、html、json 和 junit)。有关完整的命令行演示,请参阅 Apifox CLI 教程。
想要以可视化方式构建和断言你自己的 webhook 测试吗?免费试用 Apifox,无需信用卡。
Webhook 测试的逐步工作流
结合这些工具,以下是一个可重复的执行顺序。
- 检查真实载荷。 将服务商指向 webhook.site URL 并捕获一个真实事件。记录 body 结构、headers 和签名格式。
- 连通你的代码。 在本地启动你的处理器(handler),使用
ngrok http 3000或cloudflared打开一条通道,并将公网 URL 填入服务商的 webhook 配置中。 - 触发真实事件。 使用
stripe trigger、GitHub 的 Redeliver 按钮或 Slack POST,通过通道发送一个真实的事件。 - 验证签名处理。 发送带有有效签名的相同载荷,然后再发送一个被篡改过的载荷。你的处理器必须接受前者并拒绝后者。
- 断言响应和副作用。 将载荷保存为 Apifox 请求,对响应 body 和状态添加断言,并确认数据库记录已更新或下游调用已触发。
- 自动化。 将该测试场景移至 Apifox CLI 中,以便在每次代码变更时自动在 CI 中运行。
如果你是在设计 webhook 系统本身,而不仅仅是使用它,那么“如何设计可靠的 webhook”和“支付 webhook 最佳实践”涵盖了重试、幂等性和安全性。对于更广泛的图景,“webhook API 指南”以及“webhook 与事件驱动架构”解释了 webhook 在更大系统中的位置。
常见问题
如何测试 webhook?
向提供商提供一个可访问的 URL,触发一个真实或测试事件,然后确认你的处理程序(handler)接收到了 payload、验证了签名、返回了正确的状态,并执行了预期的副作用。先捕获一个真实的 payload,然后使用断言构建一个可重复的请求,以确保每次测试运行的结果都一致。
如何在本地测试 webhook?
在本地端口上运行你的处理程序,然后通过隧道将其暴露出去,以便提供商可以访问你的机器。使用 ngrok http 3000 或 cloudflared tunnel --url http://localhost:3000,将公开的 HTTPS URL 粘贴到提供商的 webhook 设置中,并触发一个事件。传入的请求会到达你的本地服务端,你可以在那里设置断点并进行实时检查。
如何使用 Postman 测试 webhook?
Postman 可以向你的接口发送一个精心构建的 POST payload 并断言其响应,但它本身无法直接接收真实的传入 webhook。Apifox 也是如此:你使用提供商的 payload 和 header 构建一个请求,在响应 body 和状态上添加断言,并将其保存为可重复使用的测试。要捕获真实的传入事件,需要将该工具与捕获服务或隧道结合使用。
如何测试 Stripe webhook?
使用 Stripe CLI。运行 stripe listen --forward-to localhost:3000/webhooks 将沙箱事件转发到你的处理程序并获取签名密钥。然后运行 stripe trigger payment_intent.succeeded(或 stripe trigger --help 中的任何其他事件)来触发一个真实的测试事件。触发操作会创建后端 API 对象并可能产生级联效应,因此 payment_intent.succeeded 也会触发 payment_intent.created。
如何测试 Slack webhook?
向传入的 webhook URL 发送 POST JSON 请求。最小的有效 payload 是 {"text":"Hello, world."},并带有 Content-type: application/json header。测试成功后会将消息发布到配置的频道中。请确保该 URL 的私密性,因为 Slack 会撤销泄露的 webhook URL。
如何测试 webhook URL?
向该 URL 发送一个示例 POST 请求,并确认它返回成功状态且行为正确。最快的检查方法是使用单个包含代表性 body 的 curl 命令。对于真实的测试,请先捕获实际的 payload,分别使用有效和无效的签名发送它,并断言其响应和副作用,而不仅仅是检查是否返回 200 状态码。
开发必备:API 全流程管理神器 Apifox
介绍完上文的内容,我想额外介绍一个对开发者同样重要的效率工具 —— Apifox。作为一个集 API 文档、调试、设计、测试、Mock、自动化测试于一体的工具,Apifox 是目前提升研发效率的首选。
如果你正在开发项目,不妨试试其极其友好的界面设计,它完全兼容 Postman 和 Swagger 数据格式,导入数据非常方便,,即使是新手也能很快上手,点击这里即可注册使用。

值得一提的是,除了个人和常规团队使用,针对有高安全合规要求、或需要在内网环境协作的企业,Apifox 还提供了深度定制的私有化部署方案。
获取专属报价与部署方案
详细的私有化部署系统架构与安全白皮书
针对您公司规模的专属报价单
免费的 1v1 专属产品演示 (Demo) 机会