拓冰建站拓冰建站
首页 / 资讯中心 / 正文

agentic-awesome-skills 测试模式技能解析:Jest/Vitest 工厂函数、Mock 策略与 TDD 红绿重构工作流

AI 技能AI 插件【免费下载链接】agentic-awesome-skillsAAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,445 agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.项目地址https://gitcode.com/gh_mirrors/an/agentic-awesome-skills点击查看免费下载本篇文章以 agentic-awesome-skills 仓库内code-showcase-testing-patterns技能文档位于 SKILL.md为核心系统讲解前端单元测试中的 TDD 工作流、行为驱动测试、工厂函数与 Mock 策略并结合仓库内 Workbench 前端apps/web-app的真实测试源码进行印证与扩展。读完本文你将掌握一套可直接复制到 React Native / React Testing Library 项目中的测试方法论包括自定义 render、组件 props 工厂、数据工厂、模块与 GraphQL Hook 的 Mock 姿势以及测试组织、异步查询与用户交互的推荐写法。技能定位与使用场景code-showcase-testing-patterns是 agentic-awesome-skills 插件库中的社区技能之一其 frontmatter 声明如下namecode-showcase-testing-patternsdescriptionJest testing patterns, factory functions, mocking strategies, and TDD workflow适用于编写单元测试、创建测试工厂、或执行 TDD red-green-refactor 周期riskcritical高风险类技能要求使用前充分验证source_typecommunity源自 ChrisWiles/claude-code-showcaseMIT 许可date_added2026-07-01该技能在仓库中存在两份副本agentic-awesome-skills 插件副本 与 agentic-awesome-skills-claude 插件副本。其适用场景非常聚焦当你需要编写单元测试、为组件/数据创建可复用的测试工厂、或遵循 TDD 的红-绿-重构循环推进功能开发时即可启用此技能。值得注意的是技能示例以 Jest API 书写而当前仓库的 Workbench 前端apps/web-app/package.json实际使用Vitest testing-library/react作为等价实现——scripts中test对应vitest、test:coverage对应vitest run --coveragedevDependencies包含vitest、testing-library/react、testing-library/jest-dom、jsdom。两者 API 高度同构describe/it/expect、jest.fn↔vi.fn、jest.mock↔vi.mock因此文中将同时给出技能原始写法与仓库内的落地形态。测试哲学TDD 与行为驱动测试测试驱动开发TDD技能文档强调的 TDD 流程是先写失败测试 → 写最少代码使其通过 → 变绿后重构并明确要求没有失败测试就不写生产代码。这一原则与本仓库中 tdd 技能 的阐述完全一致TDD 采用**垂直切片vertical slices / tracer bullets**而非水平切片即一个测试 → 一段实现 → 重复切忌先把所有测试一次性写完再一次性实现否则测试会变成对想象中行为的验证而不是对真实行为的验证。行为驱动测试测试行为behavior而不是实现implementation聚焦公共 API 与业务需求避免测试内部实现细节使用能够描述行为的测试名称判断测试是否耦合实现有一个标志重构内部函数导致测试失败、但外部行为并未改变说明该测试测的是实现而非行为。好的测试读起来像一份规格说明书例如用户可以用有效的购物车完成结账。工厂模式Factory Pattern创建getMockX(overrides?: PartialX)形式的工厂函数提供合理的默认值允许覆盖特定属性保持测试 DRY 且易于维护测试工具函数自定义 Render当组件依赖 Context / Provider如主题、国际化、路由时最推荐的做法是写一个自定义 render 函数统一包裹所有必需的 Provider避免每个测试重复嵌套。// src/utils/testUtils.tsx import { render } from testing-library/react-native; import { ThemeProvider } from ./theme; export const renderWithTheme (ui: React.ReactElement) { return render( ThemeProvider{ui}/ThemeProvider ); };用法import { renderWithTheme } from utils/testUtils; import { screen } from testing-library/react-native; it(should render component, () { renderWithTheme(MyComponent /); expect(screen.getByText(Hello)).toBeTruthy(); });仓库中的落地案例可以印证这一模式在 SkillCard.test.tsx 中组件依赖路由上下文测试便通过MemoryRouter包裹后再renderimport { render, screen } from testing-library/react; import { MemoryRouter } from react-router; it(keeps the save control outside the card link, () { render( MemoryRouter SkillCard skill{createMockSkill()} starCount{0} / /MemoryRouter, ); const link screen.getByRole(link, { name: /read skill/i }); const button screen.getByRole(button, { name: /save locally/i }); expect(link).not.toContainElement(button); });工厂模式实战组件 Props 工厂用PartialComponentPropstypeof MyComponent作为覆盖参数类型默认值 展开覆盖符...overrides即可获得类型安全且可定制的 propsimport { ComponentProps } from react; const getMockMyComponentProps ( overrides?: PartialComponentPropstypeof MyComponent ) { return { title: Default Title, count: 0, onPress: jest.fn(), isLoading: false, ...overrides, }; }; // Usage in tests it(should render with custom title, () { const props getMockMyComponentProps({ title: Custom Title }); renderWithTheme(MyComponent {...props} /); expect(screen.getByText(Custom Title)).toBeTruthy(); });数据工厂对接口返回的数据如用户、列表项同样适用interface User { id: string; name: string; email: string; role: admin | user; } const getMockUser (overrides?: PartialUser): User { return { id: 123, name: John Doe, email: johnexample.com, role: user, ...overrides, }; }; // Usage it(should display admin badge for admin users, () { const user getMockUser({ role: admin }); renderWithTheme(UserCard user{user} /); expect(screen.getByText(Admin)).toBeTruthy(); });仓库源码印证真实的 Skill 数据工厂上述模式在仓库 Workbench 前端有完整的生产级实现。位于 apps/web-app/src/factories/skill.ts 的工厂不仅支持单个对象覆盖还扩展出了批量生成与分类生成两个变体import type { Skill } from ../types; /** * Factory function for creating mock skill data */ export function createMockSkill(overrides?: PartialSkill): Skill { return { id: test-skill, name: Test Skill, description: A test skill for testing purposes, category: testing, risk: safe, source: test, date_added: 2024-01-01, path: skills/test/SKILL.md, ...overrides, }; } /** * Factory function for creating an array of mock skills */ export function createMockSkills(count: number): Skill[] { return Array.from({ length: count }, (_, i) createMockSkill({ id: skill-${i}, name: Test Skill ${i}, }) ); } /** * Factory for creating skills with different categories */ export function createMockSkillsByCategory(categories: string[]): Skill[] { return categories.flatMap((category) Array.from({ length: 3 }, (_, i) createMockSkill({ id: ${category}-skill-${i}, name: ${category} Skill ${i}, category, }) ) ); }从中可以提炼出工厂模式的进阶用法基础工厂只承担合理默认值职责复杂的批量/分类场景通过组合基础工厂实现保持每个工厂单一职责。Mocking 策略Mock 整个模块// Mock entire module jest.mock(utils/analytics); // Mock with factory function jest.mock(utils/analytics, () ({ Analytics: { logEvent: jest.fn(), }, })); // Access mock in test const mockLogEvent jest.requireMock(utils/analytics).Analytics.logEvent;Mock GraphQL Hooks对 codegen 生成的 Hook 模块进行整体 mock并在测试内通过mockReturnValue注入不同状态数据 / 加载中 / 错误jest.mock(./GetItems.generated, () ({ useGetItemsQuery: jest.fn(), })); const mockUseGetItemsQuery jest.requireMock( ./GetItems.generated ).useGetItemsQuery as jest.Mock; // In test mockUseGetItemsQuery.mockReturnValue({ data: { items: [] }, loading: false, error: undefined, });仓库源码印证模块 Mock 与全局环境 Stub仓库的测试对上述策略做了更细的落地。例如 SkillCard.test.tsx 用vi.mock替换子组件从而隔离关注点vi.mock(./SkillStarButton, () ({ SkillStarButton: () button typebuttonSave locally/button, }));SelectionFeedback.test.tsx 则展示了更复杂的全局环境 Stub 与副作用断言通过vi.stubGlobal替换URL.createObjectURL / revokeObjectURL用vi.spyOn(HTMLAnchorElement.prototype, click)捕获下载行为并用vi.spyOn(globalThis, fetch)、vi.spyOn(Storage.prototype, setItem)断言不产生网络请求、不写存储const create vi.fn((_value: Blob | MediaSource) blob:feedback-test); const revoke vi.fn((_url: string) {}); vi.stubGlobal(URL, class extends URL { static createObjectURL create; static revokeObjectURL revoke; }); const click vi.spyOn(HTMLAnchorElement.prototype, click).mockImplementation(function (this: HTMLAnchorElement) { expect(this.download).toBe(aas-feedback.json); expect(this.href).toBe(blob:feedback-test); }); const network vi.spyOn(globalThis, fetch); const storage vi.spyOn(Storage.prototype, setItem);这说明一个重要的进阶原则Mock 不只是让代码跑起来更是行为断言的载体——通过 spy 断言副作用下载文件名、URL、是否调用网络/存储是否按预期发生。测试结构组织用嵌套describe将测试按行为维度分组渲染 / 交互 / 边界并在beforeEach中清理 mockdescribe(ComponentName, () { beforeEach(() { jest.clearAllMocks(); }); describe(Rendering, () { it(should render component with default props, () {}); it(should render loading state when loading, () {}); }); describe(User interactions, () { it(should call onPress when button is clicked, async () {}); }); describe(Edge cases, () { it(should handle empty data gracefully, () {}); }); });仓库中的 useSkillShortlist.test.ts 是这一结构在 Hook 测试renderHook上的完整示范并且把边界情况组织得非常有价值空存储、已有存储、损坏的 JSON、混入非字符串条目、浏览器拒绝读存储五种初始化边界全部覆盖import { describe, it, expect, beforeEach, afterEach, vi } from vitest; import { renderHook, act, waitFor } from testing-library/react; import { useSkillShortlist } from ../useSkillShortlist; const STORAGE_KEY aas_skill_shortlist; describe(useSkillShortlist, () { beforeEach(() { localStorage.clear(); vi.clearAllMocks(); }); afterEach(() { vi.restoreAllMocks(); }); describe(Initialization, () { it(starts empty when nothing is stored, () { ... }); it(reads an existing shortlist from localStorage, () { ... }); it(falls back to an empty list for corrupted storage, () { localStorage.setItem(STORAGE_KEY, not-json{); const { result } renderHook(() useSkillShortlist()); expect(result.current.ids).toEqual([]); }); it(drops non-string entries from stored JSON, () { localStorage.setItem(STORAGE_KEY, JSON.stringify([skill-1, 42, null, skill-2])); const { result } renderHook(() useSkillShortlist()); expect(result.current.ids).toEqual([skill-1, skill-2]); }); it(stays usable when the browser rejects storage reads, () { vi.spyOn(Storage.prototype, getItem).mockImplementation(() { throw new DOMException(Storage access denied, SecurityError); }); expect(() renderHook(() useSkillShortlist())).not.toThrow(); }); }); });值得注意的细节beforeEach中vi.clearAllMocks()与afterEach中vi.restoreAllMocks()配合确保 mock 状态在不同测试之间不泄漏——这正是技能文档Clear mocks between tests最佳实践的落地。查询模式Query PatternsTesting Library 的三大查询方法的取舍// Element must exist expect(screen.getByText(Hello)).toBeTruthy(); // Element should not exist expect(screen.queryByText(Goodbye)).toBeNull(); // Element appears asynchronously await waitFor(() { expect(screen.findByText(Loaded)).toBeTruthy(); });getBy*找不到就抛错用于断言元素必须存在queryBy*找不到返回 null用于断言元素不应存在findBy*异步轮询直到出现用于断言异步加载的元素。仓库中大量使用了这一套模式例如 App.workbench-isolation.test.tsx 用screen.findByRole(heading, ...)等待异步渲染、用waitFor(() expect(screen.queryByText(Loading...)).not.toBeInTheDocument())断言加载态消失。用户交互模式User Interaction Patterns通过fireEvent模拟输入与点击然后waitFor断言回调被调用import { fireEvent, screen } from testing-library/react-native; it(should submit form on button click, async () { const onSubmit jest.fn(); renderWithTheme(LoginForm onSubmit{onSubmit} /); fireEvent.changeText(screen.getByLabelText(Email), userexample.com); fireEvent.changeText(screen.getByLabelText(Password), password123); fireEvent.press(screen.getByTestId(login-button)); await waitFor(() { expect(onSubmit).toHaveBeenCalled(); }); });仓库版本web 环境与之几乎一一对应见 SelectionFeedback.test.tsx用fireEvent.click(screen.getByText(Record feedback locally))触发流程、用fireEvent.change(screen.getByLabelText(/Minutes to a useful selection/), { target: { value: 4.5 } })模拟表单输入、用fireEvent.change(screen.getByLabelText(Installation experience), ...)触发依赖联动编辑后使保存按钮消失。反模式必须避免的写法反模式一测试 Mock 行为而非真实行为// Bad - testing the mock expect(mockFetchData).toHaveBeenCalled(); // Good - testing actual behavior expect(screen.getByText(John Doe)).toBeTruthy();判断标准如果断言的是某个 mock 函数被调用了而非其调用的结果对 UI 产生的影响该测试就在测 mock 本身对真实行为毫无保护力。仓库 SelectionFeedback.test.tsx 的写法可作正面参照——它虽然 spy 了click/fetch/storage但核心断言仍是生成的 JSON 内容正确、预览按钮与保存按钮的呈现符合预期这一可观察行为。反模式二不用工厂导致数据重复且不一致// Bad - duplicated, inconsistent test data it(test 1, () { const user { id: 1, name: John, email: johntest.com, role: user }; }); it(test 2, () { const user { id: 2, name: Jane, email: janetest.com }; // Missing role! }); // Good - reusable factory const user getMockUser({ name: Custom Name });手写内联数据有两个问题重复样板代码以及字段遗漏导致的数据不一致上面第二个 test 漏掉了role字段。工厂函数把合法最小数据集收敛到一处测试只表达差异。最佳实践清单始终为 props 和数据使用工厂函数Always use factory functions for props and data测试行为而不是实现Test behavior, not implementation使用描述性的测试名称Use descriptive test names用 describe 块组织测试Organize with describe blocks在测试之间清理 mockClear mocks between tests保持测试聚焦——每个测试只验证一个行为Keep tests focused - one behavior per test运行测试技能文档给出的 Jest 命令# Run all tests npm test # Run with coverage npm run test:coverage # Run specific file npm test ComponentName.test.tsx在仓库的 apps/web-app/package.json 中等价命令是 Vitest 形态test: vitest, test:coverage: vitest run --coverage因此实际执行cd apps/web-app # 运行全部测试监听模式 npm test # 单次运行并统计覆盖率 npm run test:coverage # 只运行指定测试文件 npx vitest run src/hooks/__tests__/useSkillShortlist.test.ts覆盖率的支持来自devDependencies中的vitest/coverage-v8测试环境由jsdom提供。另外注意仓库的engines要求 Node^22.22.2 || ^24.15.0 || 26.0.0运行测试前请先确认本机 Node 版本满足要求。与其他技能的协同react-ui-patterns配套测试 UI 的全部分支状态——loading、error、empty、success仓库中 react-ui-patterns 技能systematic-debugging在修复 bug 之前先写一个能复现该 bug 的测试对应仓库 systematic-debugging 技能的思路让测试成为调试过程的回归防线。使用限制与注意事项技能文档自身的Limitations部分明确提醒仅在任务与上游来源及本地项目上下文明确匹配时使用本技能在应用改动之前务必验证命令、生成的代码、依赖、凭据与外部服务行为不要将示例当作环境特定测试、安全审查或用户授权的替代品——涉及破坏性、昂贵或生产环境的操作必须先获得用户批准。结合其risk: critical属性本技能在启用时应当被当作方法论参考而非自动执行指令所有生成代码都应经过本地真实环境的验证例如先运行npm test确认测试真实通过再继续推进。小结code-showcase-testing-patterns技能本质上是一套前端单元测试工程规范以 TDD 红-绿-重构为流程主线、以行为驱动测试为断言哲学、以工厂函数与 Mock 策略为可复用基建并以自定义 render、查询模式与交互模式作为 Testing Library 的具体手法。本文结合仓库 Workbench 前端的真实测试factories/skill.ts、SkillCard.test.tsx、useSkillShortlist.test.ts、SelectionFeedback.test.tsx展示了这些模式在生产代码库中的落地形态——从文档里的模式到仓库里的实现两者互相印证可直接作为你下一个组件或 Hook 编写测试时的参照模板。赞分享AI 技能AI 插件【免费下载链接】agentic-awesome-skillsAAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,445 agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.项目地址https://gitcode.com/gh_mirrors/an/agentic-awesome-skills点击查看免费下载相关推荐Superpowers 的 TDD 技能解析红-绿-重构循环与先看测试失败的 Agent 测试纪律Superpowers 的 TDD 技能解析红 绿 重构循环与先看测试失败的 Agent 测试纪律 Superpowers 是一个面向编码 Agent 的AI 技能AI 插件开发工具RuView SPARC 实现专家 Agent 模板解析TDD 红绿重构工作流与 Claude Code Agent 定义实战RuView SPARC 实现专家 Agent 模板解析TDD 红绿重构工作流与 Claude Code Agent 定义实战 本篇基于 RuView 仓库中人工智能计算机视觉物联网智能家居后端嵌入式awesome-copilot 测试自动化插件指南从 TDD 红绿重构到 Playwright 端到端测试awesome copilot 测试自动化插件指南从 TDD 红绿重构到 Playwright 端到端测试 本指南基于 awesome copilot htt文档知识库AI 技能/插件上一篇WandEnhancer 上手笔记10 分钟把 WeMod 的高级功能装进本地下一篇Sunshine 游戏串流服务 Docker 部署全攻略一条命令让 Moonlight 串流稳定跑起来创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门