资讯动态

Langflow E2E 自定义 Fixture 详解:让 Playwright 测试自动捕获 API 错误与流程执行失败

发布时间:2026/9/7 17:46:01 来源:尧图企业网站定制
Langflow E2E 自定义 Fixture 详解让 Playwright 测试自动捕获 API 错误与流程执行失败【免费下载链接】langflowLangflow is a powerful tool for building and deploying AI-powered agents and workflows.项目地址: https://gitcode.com/GitHub_Trending/la/langflowLangflow 的前端 E2E 测试套件在 Playwright 默认test/expect之上封装了一层自定义 fixturefixtures.ts它会自动监控所有/api/响应在后端返回 5xx、或流程构建/运行流中出现 Python 异常时将测试判为失败。本文基于 Custom Test Fixtures 参考文档 展开结合 fixture 的源码实现讲解这套“静默失败拦截”机制的检测范围、预期错误声明方式、超时与脱敏行为以及全局清理流程。为什么需要自定义 FixtureLangflow 的 E2E 场景是前端画布操作触发后端构建build或运行run结果往往通过 SSE/NDJSON 流返回。如果只用原生 Playwright测试很容易出现“绿了但其实是坏的”情况后端返回了 500 Internal Server Error但测试只断言了 UI 文案照样通过一次 flow 构建因 Python 异常静默失败但测试只检查了按钮状态API 调用返回 404资源已被删除测试根本没有检查响应体。自定义 fixture 的核心思路是在测试运行期间拦截页面发出的所有/api/请求响应把上述静默失败变成确定性的测试失败。这样开发者无需在每个用例里手写响应断言。Fixture 入口与导入规则fixture 文件位于 src/frontend/tests/fixtures.ts。它通过base.extend扩展 Playwright 的pagefixturefixtures.ts#L86-L98并重新导出expect。硬性规则测试文件必须从../../fixtures导入test与expect而不是从playwright/test导入。后者会绕过全部错误检测让静默失败重新成为可能// 正确 —— 包含自动错误检测 import { expect, test } from ../../fixtures; // 错误 —— 没有错误检测可能出现静默失败 import { expect, test } from playwright/test;导入自定义 fixture 后page的类型不再是原生Page而是 utils/types.ts 中定义的LangflowPage它在原生Page上附加了三个辅助方法export type LangflowPage Page { allowFlowErrors: () void; expectServerError: (expectation: ExpectedServerError) void; runA11yScan: ( label: string, options?: A11yScanOptions, ) PromiseICheckerResult | null; };其中前两个与错误检测直接相关runA11yScan则用于无障碍扫描由环境变量RUN_A11Y控制开关。第一层检测HTTP 错误响应fixture 在pagefixture 内注册了request/response/requestfinished/requestfailed四类监听器fixtures.ts#L337-L391核心检查逻辑在inspectResponse中fixtures.ts#L186-L208if (url.includes(/api/) status 400) { const method response.request().method().toUpperCase(); const path new URL(url).pathname; const observed: ObservedHttpError { method, path, status, statusText: response.statusText(), responseBody: await getResponseBody(response, ${method} ${path}), }; if (status 500) { if (clientErrors.length MAX_CLIENT_ERROR_DIAGNOSTICS) { clientErrors.push(observed); } } else { observeServerError(serverErrorContract, observed); } }参考文档对每个状态码的含义解释如下状态码含义为什么值得关注400Bad Request客户端发送了非法数据——大概率是前端 bug404Not Found资源不存在——大概率是过期 ID 或缺少测试前置准备422Validation ErrorPydantic 校验失败——大概率是 schema 不匹配500Internal Server Error后端崩溃——一定是 bug从源码结构看当前实现把 4xx 与 5xx 做了区分处理4xx 响应记入clientErrors诊断列表上限 20 条MAX_CLIENT_ERROR_DIAGNOSTICS测试结束时以 JSON 附件api-4xx-responses的形式挂到测试报告里fixtures.ts#L441-L446供排查时定位问题5xx 响应进入“服务端错误契约”server error contract未注册的 5xx 会直接使测试失败见下文“声明预期错误”一节。另外请求追踪有一个豁免项shouldTrackApiRequest会跳过/api/v1/files/profile_pictures/路径server-error-contract.mjs#L192-L203因为头像这类静态资源在浏览器关闭时可能被取消若计入“未完成的 API 请求”会造成假性失败。第二层检测流程执行错误流式响应对于 HTTP 200 但内容里藏着错误的情形fixture 专门监控三类事件/执行端点fixtures.ts#L211-L216URL 含/events?event_delivery事件投递端点覆盖 streaming/polling/direct 三种模式URL 含/build/组件构建URL 含/run/流程运行。处理流程是跳过流式内容若响应头content-type属于text/event-stream、application/grpc、application/octet-stream、application/x-ndjson中的流式提示fixtures.ts#L220-L231说明响应体尚未完整直接返回不做正文解析带超时读取正文通过readResponseBodyWithTimeout在 2 秒内读取响应体下文详述超时或读取失败则记录诊断信息并跳过逐行解析 JSON把响应体按行拆分对每一行尝试JSON.parse命中以下任一条件即判定为流程错误fixtures.ts#L252-L280json.data.build_data.params以Error开头组件构建错误json.data.error true或json.error true注意是布尔true而非false错误文案取自error_message字段Python 异常兜底若 JSON 解析没有命中则用一组正则匹配原始文本中的 Python 异常fixtures.ts#L287-L295const exceptionPatterns [ /NameError: ./, /TypeError: ./, /ValueError: ./, /AttributeError: ./, /ImportError: ./, /KeyError: ./, /An error occured ./, ];命中后错误会以{ path, status: 200, statusText: Flow Error, type: flow_error }的形式记入错误列表上限 20 条MAX_FLOW_ERROR_DIAGNOSTICS 20。声明预期错误allowFlowErrors 与 expectServerError有些测试本身就是故意触发错误验证错误处理、校验反馈等。fixture 提供两个粒度不同的豁免/声明机制。allowFlowErrors()豁免流程执行错误在测试开头调用page.allowFlowErrors()即可让本测试中的流程执行错误不再导致失败test(should show error message on invalid component config, { tag: [release] }, async ({ page }) { page.allowFlowErrors(); // 仅对当前测试放行流程错误 await awaitBootstrapTest(page); // ... 触发错误的操作 ... await expect(page.getByText(/error/i)).toBeVisible(); });源码实现非常直接fixture 内部维护一个allowFlowErrors布尔标志测试结束检查错误列表时只有flowErrors.length 0 !allowFlowErrors才抛错fixtures.ts#L124-L130 对应实现见 fixtures.ts#L124-L130 与 fixtures.ts#L505-L524。注意allowFlowErrors()只豁免流程执行错误。HTTP 5xx 仍会失败——后端崩溃不属于预期行为必须显式处理。仓库中真实用例可见 knowledge-bases.a11y.spec.ts 中多处page.allowFlowErrors()的用法。expectServerError()为 5xx 建立“契约”对于确实预期后端返回 5xx 的场景例如模拟写入失败可以在测试中先注册预期5xx 出现时才会被“认领”而不算意外错误page.expectServerError({ method: POST, path: /api/v1/variables/, status: 500, count: 1, });该模式在 db-providers.a11y.spec.ts#L105-L117 中有实际使用配合page.route拦截并让写入接口失败。契约的校验与匹配逻辑在 server-error-contract.mjs 中注册时强校验expectServerErrorserver-error-contract.mjs#L239-L257path必须以/开头且不含查询串status必须是 500–599 的整数count必须是 ≥1 的整数否则立即抛错匹配规则observeServerErrorserver-error-contract.mjs#L259-L276按method path status三元组配对且同一预期只能被认领count次双向失败getServerErrorContractFailures未认领的 5xxunexpected或声明了却没出现/出现次数不符missing都会让测试失败。也就是说契约既是“白名单”也是“断言”。错误上报失败消息长什么样fixture 采用“测试函数执行期间收集、测试函数结束use(page)之后统一清算”的策略。清算顺序大致是用 2 秒宽限期API_REQUEST_DRAIN_TIMEOUT_MS 2000等待在途 API 请求收敛fixtures.ts#L393-L411确保“响应触发的后续请求”也能被观察到将 4xx 诊断挂为api-4xx-responses附件检查服务端错误契约若有意外/缺失的 5xx 或超 2 秒仍未解决的请求抛出Server-error contract failed并逐条列出method path - status同时提示“Register intentional failures with page.expectServerError(...)”fixtures.ts#L490-L502若存在未被豁免的流程错误抛出形如下面的消息fixtures.ts#L505-L524Test failed due to 1 flow execution error(s): - /api/v1/build/abc123/flow Traceback (most recent call last)... If this error is expected, call page.allowFlowErrors() at the start of your test.参考文档中给出的另一类失败示例意外 5xx对应的是契约失败分支Server-error contract failed: - unexpected POST /api/v1/build/abc123/flow - 500: ...值得注意的安全细节所有进入失败消息的响应体片段都会先经过sanitizeResponseExcerpt处理server-error-contract.mjs#L67-L79——它会把api_key、token、password、authorization等敏感字段的值替换为[REDACTED]并截断到 1000 字符避免把密钥泄漏进测试报告。超时行为为什么 fixture 不会卡死长流文档中提到“读取响应体使用 2 秒超时”其落地实现是 server-error-contract.mjs 中的readResponseBodyWithTimeoutserver-error-contract.mjs#L81-L127用Promise.race竞速response.text()与 2 秒定时器超时返回诊断信息而非正文。fixture 顶部把这组常量集中在了一起fixtures.ts#L41-L45const MAX_FLOW_ERROR_DIAGNOSTICS 20; const API_REQUEST_DRAIN_TIMEOUT_MS 2000; const RESPONSE_BODY_READ_TIMEOUT_MS API_REQUEST_DRAIN_TIMEOUT_MS; const RESPONSE_INSPECTION_DRAIN_TIMEOUT_MS API_REQUEST_DRAIN_TIMEOUT_MS;设计意图是仍在传输中的流式响应如果 2 秒内读不到完整正文就跳过该响应的正文解析避免 fixture 无限阻塞在长流上同理响应体检查任务本身也有 2 秒的清算上限超时的检查任务会被记录为诊断错误。此外shouldSettleApiRequestOnResponseserver-error-contract.mjs#L205-L233区分了“长生命周期流”text/event-stream、gRPC或event_deliverystreaming/direct的 NDJSON与有限响应——前者在响应头到达后即可结算后者要一直跟踪到requestfinished/requestfailed保证由响应触发的后续请求在测试收尾时仍然可见。全局清理globalTeardown 删除临时数据库playwright.config.ts 的webServer配置会为 E2E 启动三组进程一个 OpenAI 兼容的本地 mock 服务端口 8787、uvicorn 后端端口 7860与前端npm start端口 3000。其中后端被注入LANGFLOW_DATABASE_URL: sqlite:///./temp、LANGFLOW_AUTO_LOGIN: true等测试环境变量playwright.config.ts#L114-L153即整轮测试跑在一个位于src/frontend/temp的临时 SQLite 库上。配置同时声明了全局清理钩子playwright.config.ts#L52globalTeardown: require.resolve(./tests/globalTeardown.ts),globalTeardown.ts 在所有测试结束后删除该临时数据库目录保证下一轮测试从干净状态开始globalTeardown.ts#L48-L80。实现上有两点工程细节removeWithRetry最多重试 5 次、按200 * 2^i毫秒指数退避Windows 上 uvicorn 进程可能仍持有 SQLite 文件句柄POSIX 允许删除打开中的文件而 Win32 会报EBUSY/EPERM若整体删除失败则逐个子项删除removeChildrenBestEffort再重试且绝不从 teardown 中抛出异常最坏情况下留下日志提示交给 CI 工作区清理。小结把哪些测试写成什么样子综合参考文档与源码编写 Langflow E2E 用例时的 fixture 相关要点可以浓缩为一律import { expect, test } from ../../fixtures从pagefixture 解构出的page即LangflowPage任何意外 5xx 与未被豁免的流程错误都会让测试失败无需手写响应断言4xx 会以附件形式出现在报告中辅助定位故意测试错误处理时测试开头调用page.allowFlowErrors()仅限流程错误5xx 不受豁免故意让后端返回 5xx 时用page.expectServerError({ method, path, status, count })注册精确契约出现次数必须与声明一致长流与慢响应有 2 秒级的超时保护fixture 不会拖垮测试测试结束后临时数据库由globalTeardown统一清理。这套机制让 Langflow 的前端 E2E 测试从“只看 UI 表象”升级为“UI 断言 后端健康兜底”是 e2e-testing 技能文档 中推荐的工作方式之一。【免费下载链接】langflowLangflow is a powerful tool for building and deploying AI-powered agents and workflows.项目地址: https://gitcode.com/GitHub_Trending/la/langflow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价