ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Anomalib Studio UI 测试工具指南:render 与 renderHook 的共享 Provider 栈实践

Anomalib Studio UI 测试工具指南:render 与 renderHook 的共享 Provider 栈实践 Anomalib Studio UI 测试工具指南render 与 renderHook 的共享 Provider 栈实践【免费下载链接】anomalibAn anomaly detection library comprising state-of-the-art algorithms and features such as experiment management, hyper-parameter optimization, and edge inference.项目地址: https://gitcode.com/GitHub_Trending/an/anomalib导读在 Anomalib Studio 的前端工程application/ui/中组件与 Hook 的单元测试往往依赖 React Router 路由参数、React Query 缓存、主题、Toast、流连接与 Suspense 等应用级上下文。若每个测试文件都手工拼装 Provider 栈不仅冗长易错还会让测试环境与应用真实运行环境产生偏差。本文以.agents/skills/ui-test-utils/SKILL.md为核心结合application/ui/tests/utils.ts的源码实现与仓库内真实测试用例完整讲解render/renderHook共享工具的设计动机、Provider 组成、选项参数与 MSW 协作方式帮助你写出短小、确定、贴近生产环境的前端测试。为什么需要共享的 render / renderHook 工具Anomalib Studio 的被测单元组件或 Hook通常深度依赖应用上下文。如果直接从testing-library/react导入render/renderHook测试中就必须自行补上所有 Provider导致每个测试文件重复粘贴大段样板代码且容易遗漏依赖、造成缓存串扰。仓库在 application/ui/tests/utils.ts 中导出了两个共享工具render渲染组件、对话框或视图断言其 DOM 输出renderHook渲染读取 Provider 状态、路由参数、搜索参数或 React Query 状态的 Hook。二者都基于testing-library/react的底层实现但额外构建了一个内存路由器并挂载完整的应用 Provider 栈测试行为与应用真实环境保持一致。Provider 栈构成一份来自源码的精确清单根据 application/ui/tests/utils.ts 中TestProviders的实现工具默认挂载的 Provider 自外向内依次为Provider作用源码依据QueryClientProvider默认提供一个隔离的QueryClient防止 React Query 缓存跨测试泄漏tests/utils.tsThemeProvider为geti-ui/ui组件提供主题上下文同上NuqsAdapter支撑 URL query-state 相关的 Hook来自nuqs/adapters/react-router/v8同上StreamConnectionProvider为依赖视频流的 UI 提供流连接状态idle/connecting/connected/failed/disconnected与start/stop能力stream-connection-provider.tsxSuspense为懒加载组件提供IntelBrandedLoading回退tests/utils.tsToast提供 Toast 断言与反馈行为基于sonner的Toastertoast.component.tsx注意StreamConnectionProvider若在 Provider 外部使用会抛出useStreamConnection was used outside of StreamConnectionProvider异常这正是共享工具必须包含它的原因之一见 stream-connection-provider.tsx。默认路由与默认项目 IDcreateTestRouter见 tests/utils.ts定义了工具的默认行为默认path/projects/:projectId/inspect默认route/projects/project-123/inspect其中DEFAULT_PROJECT_ID project-123见 tests/utils.ts。因此不传任何选项时测试就运行在项目 123 的 inspect 页面这一默认上下文中绝大多数 inspect 功能测试无需显式指定路由。何时用 render何时用 renderHook被测对象是组件、对话框或视图需要验证渲染出的 DOM → 使用render。被测对象是Hook需要读取 Provider 状态、路由参数、URL 搜索参数或 React Query 状态 → 使用renderHook。规则很简单只有当被测单元完全不依赖任何应用上下文时才考虑直接使用 Testing Library 的原生渲染凡是依赖应用上下文的测试都从tests/utils导入共享工具而不要在单个测试里重复搭建 Provider 栈。组件测试实操基础用法导入与指定路由组件测试从测试文件出发按相对路径导入tests/utils.ts中的render并保留testing-library/react的screen用于断言import { screen } from testing-library/react; import { render } from ../../../../../tests/utils; import { ProjectPanel } from ./project-panel.component; it(shows the selected project, () { render(ProjectPanel /, { route: /projects/project-456/inspect }); expect(screen.getByText(Project details)).toBeVisible(); });当组件依赖特定位置或特定projectId时通过route传入目标 URL传入的route必须与当前path匹配。path 与 route 成对使用当被测组件位于非默认路由例如设置页时需要同时给出路由模式与实际地址render(ProjectSettings /, { path: /projects/:projectId/settings, route: /projects/project-456/settings, });path用于构造内存路由的路由表route用于设置初始历史条目两者不匹配会导致渲染失败。异步与后端行为配合 MSW渲染完成后优先使用 Testing Library 的可访问性查询getByRole、getByLabelText等与异步助手findBy...、waitFor。对于依赖后端接口的行为先在调用render之前通过仓库已有的 MSWserver配置响应server.use( http.get(/api/projects/{project_id}, () HttpResponse.json(project)), ); render(ProjectPanel /, { route: /projects/project-456/inspect }); expect( await screen.findByRole(heading, { name: project.name }), ).toBeVisible();MSW 服务端在 application/ui/src/msw-node-setup.ts 中通过setupServer(...handlers)初始化其handlers来自 application/ui/src/api/utils.ts 中基于 OpenAPI 规范自动生成的处理器。仓库中的真实测试如 use-active-pipeline-status.test.tsx正是用server.use(http.get(/api/active-pipeline, ...))覆盖响应后配合waitFor断言异步结果的。Hook 测试实操基础用法优先使用共享 renderHook不要在测试里自建wrapper直接使用共享工具import { waitFor } from testing-library/react; import { renderHook } from ../../../../../tests/utils; import { useActivePipelineStatus } from ./use-active-pipeline-status.hook; it(reports an active project, async () { server.use( http.get(/api/active-pipeline, () HttpResponse.json({ project_id: project-456 }), ), ); const { result } renderHook(() useActivePipelineStatus(project-123)); await waitFor(() { expect(result.current.hasActiveProject).toBe(true); }); });这是仓库内 use-active-pipeline-status.test.tsx 实际采用的模式Hook 内部通过 React Query 请求/api/active-pipeline测试用server.use注入响应再用waitFor等待异步状态收敛。选项组合route / path / queryClient / initialPropsrenderHook接受与render相同的route、path、queryClient选项并额外透传 Testing Library 的标准 Hook 选项如initialProps支持通过rerender验证 props 变化const queryClient new QueryClient(); const { result, rerender } renderHook( ({ projectId }) useActivePipelineStatus(projectId), { initialProps: { projectId: project-123 }, queryClient, route: /projects/project-123/inspect, }, ); rerender({ projectId: project-456 });queryClient 的取舍默认情况不传queryClient工具会为每次调用新建隔离的QueryClient杜绝 React Query 缓存跨用例泄漏见 tests/utils.ts特殊场景仅当测试需要预置、检视或有意共享缓存状态时才显式传入queryClient。测试卫生规则Test HygieneSKILL.md 明确了以下纪律仓库的测试配置也在工具层面提供了支撑用 MSW 模拟接口杜绝真实网络请求所有 API 行为通过server.use(...)配置setup-tests.ts在beforeAll启动server.listen、afterEach执行server.resetHandlers()、afterAll关闭服务见 application/ui/src/setup-tests.ts。异步结果一律用findBy...或waitFor等待不要用同步断言猜测时序。行为依赖项目标识或 URL 状态时显式给出route避免隐式依赖默认路径。如果被测代码引用的是外部导入的共享queryClient而非工具提供的客户端须在beforeEach中调用queryClient.clear()——这正是 use-active-pipeline-status.test.tsx 的做法。不要在render/renderHook外层再包裹应用 ProviderProvider 栈已由共享工具全权负责重复包裹反而会造成上下文冲突。测试环境与工具链配置理解共享工具还需了解其运行底座Vitest 配置单元测试运行在jsdom环境globals: true可直接使用describe/expect测试文件按./src/**/*.test.{ts,tsx}匹配setupFiles指向src/setup-tests.ts见 application/ui/vitest.config.ts。由于geti-ui/ui以 ESM 形式直接导入 React Spectrum 的 CSS这些包被强制inline以便 Vite 转换处理。全局测试准备setup-tests.ts引入testing-library/jest-dom、node-fetch填充fetch/Request、并 stub 掉ResizeObserver与IntersectionObserver为 UI 组件测试提供完整的 DOM 能力。Playwright 组件测试若使用组件测试而非 Vitest 单元测试仓库在 application/ui/tests/fixtures.ts 中通过createNetworkFixture提供了基于 OpenAPI 自动生成的 MSW 处理器以空baseUrl匹配localhost:3000上的相对 API 路径可作为端到端交互测试的补充。小结Anomalib Studio 的共享render/renderHook工具把内存路由 完整 Provider 栈 隔离 QueryClient固化为默认测试环境让组件与 Hook 测试天然运行在贴近生产应用的上下文中同时保持路由行为的确定性与缓存状态的隔离性。编写新测试时的核心心法从application/ui/tests/utils.ts导入render/renderHook而非直接使用 Testing Library 原生 API需要特定路由时成对传递path与route默认场景连选项都可省略接口行为一律交给 MSW 的server.use异步结果用findBy.../waitFor等待不要重复包裹 Provider不要滥用显式queryClient。遵循这套约定你可以在保持测试简短的同时获得与 Anomalib Studio 实际运行环境完全一致的测试体验。【免费下载链接】anomalibAn anomaly detection library comprising state-of-the-art algorithms and features such as experiment management, hyper-parameter optimization, and edge inference.项目地址: https://gitcode.com/GitHub_Trending/an/anomalib创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进