[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71H8WTAPGCKZ6H2K2GFPAQG":3,"skill-same:01M71H8WTAPGCKZ6H2K2GFPAQG":61},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":36,"linkOnly":37,"markdownContent":38,"markdownBody":39,"frontMatter":40,"refName":41,"commitSha":42,"skillDirectory":43,"forksCount":13,"repositoryHtmlUrl":44,"repositoryHomepageUrl":6,"ownerHtmlUrl":45,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":46,"install":47,"resources":60},"01M71H8WTAPGCKZ6H2K2GFPAQG","01-test-services",null,"Effect-TS 4 testing with layers — mock services, test Refs, Layer.succeed, and test dependency injection","lambda-solver\u002Flambdabench-pro","lambda-solver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F279071046?v=4","01M71H55W2WK3PM6RRAY9JZWAJ","01M71H55WRT9XAFDAVACTCC2XP",0,"MIT","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Feffect-ts\u002Ftesting\u002F01-test-services\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32,33,34,35],"test","services","01 test services","effect","dependency injection","layers","testing","mock","with","opencode","dependency","injection","layer","2026-11-10 10:12:30",false,"---\nname: 01-test-services\ndescription: Effect-TS 4 testing with layers — mock services, test Refs, Layer.succeed, and test dependency injection\nlicense: MIT\ncompatibility: opencode\n---\n\n# Testing Services with Layers\n\n## Core pattern: mock at the Layer level\n\nProvide a `Layer.succeed` or `Layer.effect` that replaces the real implementation.\nNever reach past the layer boundary in tests.\n\n```typescript\nimport { Context, Effect, Layer, Ref } from \"effect\";\n\n\u002F\u002F Mock a service with a Ref for state inspection\nconst mockDb = Layer.effect(\n  Database,\n  Effect.gen(function* () {\n    const store = yield* Ref.make\u003CArray\u003CRow>>([]);\n    return Database.of({\n      query: Effect.fnUntraced(function* (_sql) {\n        return yield* Ref.get(store);\n      }),\n      insert: Effect.fnUntraced(function* (row) {\n        yield* Ref.update(store, (rows) => [...rows, row]);\n      }),\n    });\n  }),\n);\n```\n\n## Test Ref service — expose internal state to tests\n\n```typescript\nexport class TodoRepoTestRef extends Context.Service\u003CTodoRepoTestRef, Ref.Ref\u003CArray\u003CTodo>>>()(\"app\u002FTodoRepoTestRef\") {\n  static readonly layer = Layer.effect(TodoRepoTestRef, Ref.make([]));\n}\n\n\u002F\u002F layerTest: expose TestRef via provideMerge so tests can inspect\nclass TodoRepo extends Context.Service\u003C\n  TodoRepo,\n  {\n    create(title: string): Effect.Effect\u003CTodo>;\n    readonly list: Effect.Effect\u003CReadonlyArray\u003CTodo>>;\n  }\n>()(\"app\u002FTodoRepo\") {\n  static readonly layerTest = Layer.effect(\n    TodoRepo,\n    Effect.gen(function* () {\n      const store = yield* TodoRepoTestRef;\n      return TodoRepo.of({\n        create: Effect.fnUntraced(function* (title: string) {\n          const todos = yield* Ref.get(store);\n          const todo = { id: todos.length + 1, title };\n          yield* Ref.set(store, [...todos, todo]);\n          return todo;\n        }),\n        list: Ref.get(store),\n      });\n    }),\n  ).pipe(Layer.provideMerge(TodoRepoTestRef.layer));\n}\n\n\u002F\u002F In test: access raw store\nconst todos = yield * Ref.get(yield * TodoRepoTestRef);\n```\n\n## Mocking LanguageModel (AI SDK)\n\nThe AI SDK uses `Context.Service` exactly like any other service:\n\n```typescript\nimport { LanguageModel } from \"effect\u002Funstable\u002Fai\";\n\nconst mockLmLayer = (responses: ReadonlyArray\u003Cstring>) =>\n  Layer.effect(\n    LanguageModel.LanguageModel,\n    Effect.gen(function* () {\n      const idx = yield* Ref.make(0);\n      return {\n        generateText: Effect.fnUntraced(function* (_options) {\n          const i = yield* Ref.getAndUpdate(idx, (n) => n + 1);\n          return {\n            text: responses[Math.min(i, responses.length - 1)] ?? \"\",\n            usage: { inputTokens: 0, outputTokens: 0 },\n            toolCalls: [],\n            finishReason: \"stop\" as const,\n          };\n        }),\n        generateObject: () => Effect.die(new Error(\"not mocked\")),\n        streamText: () => Effect.die(new Error(\"not mocked\")),\n      } as unknown as LanguageModel.Service;\n    }),\n  );\n```\n\n## Per-test provide — isolated\n\n```typescript\ntest(\"processes item\", async () => {\n  await Effect.runPromise(\n    Effect.gen(function* () {\n      const svc = yield* MyService;\n      const result = yield* svc.doThing(\"input\");\n      expect(result).toBe(\"expected\");\n    }).pipe(Effect.provide(MyService.layerTest)),\n  );\n});\n```\n\n## Shared layer across describe block — use `layer()` from @effect\u002Fvitest\n\n```typescript\nimport { layer } from \"@effect\u002Fvitest\";\n\nlayer(TodoRepo.layerTest)(\"TodoRepo tests\", (it) => {\n  it.effect(\"creates a todo\", () =>\n    Effect.gen(function* () {\n      const repo = yield* TodoRepo;\n      yield* repo.create(\"Write tests\");\n      const all = yield* repo.list;\n      expect(all.length).toBeGreaterThanOrEqual(1);\n    }),\n  );\n});\n```\n\n## Counting calls with Ref\n\n```typescript\ntest(\"calls LLM twice for maxDepth=0\", async () => {\n  await Effect.runPromise(\n    Effect.gen(function* () {\n      const callCount = yield* Ref.make(0);\n      const layer = mockLmLayerCounting(callCount);\n      yield* myEffect.pipe(Effect.provide(layer));\n      const total = yield* Ref.get(callCount);\n      expect(total).toBeGreaterThanOrEqual(2);\n    }),\n  );\n});\n```\n","# Testing Services with Layers\n\n## Core pattern: mock at the Layer level\n\nProvide a `Layer.succeed` or `Layer.effect` that replaces the real implementation.\nNever reach past the layer boundary in tests.\n\n```typescript\nimport { Context, Effect, Layer, Ref } from \"effect\";\n\n\u002F\u002F Mock a service with a Ref for state inspection\nconst mockDb = Layer.effect(\n  Database,\n  Effect.gen(function* () {\n    const store = yield* Ref.make\u003CArray\u003CRow>>([]);\n    return Database.of({\n      query: Effect.fnUntraced(function* (_sql) {\n        return yield* Ref.get(store);\n      }),\n      insert: Effect.fnUntraced(function* (row) {\n        yield* Ref.update(store, (rows) => [...rows, row]);\n      }),\n    });\n  }),\n);\n```\n\n## Test Ref service — expose internal state to tests\n\n```typescript\nexport class TodoRepoTestRef extends Context.Service\u003CTodoRepoTestRef, Ref.Ref\u003CArray\u003CTodo>>>()(\"app\u002FTodoRepoTestRef\") {\n  static readonly layer = Layer.effect(TodoRepoTestRef, Ref.make([]));\n}\n\n\u002F\u002F layerTest: expose TestRef via provideMerge so tests can inspect\nclass TodoRepo extends Context.Service\u003C\n  TodoRepo,\n  {\n    create(title: string): Effect.Effect\u003CTodo>;\n    readonly list: Effect.Effect\u003CReadonlyArray\u003CTodo>>;\n  }\n>()(\"app\u002FTodoRepo\") {\n  static readonly layerTest = Layer.effect(\n    TodoRepo,\n    Effect.gen(function* () {\n      const store = yield* TodoRepoTestRef;\n      return TodoRepo.of({\n        create: Effect.fnUntraced(function* (title: string) {\n          const todos = yield* Ref.get(store);\n          const todo = { id: todos.length + 1, title };\n          yield* Ref.set(store, [...todos, todo]);\n          return todo;\n        }),\n        list: Ref.get(store),\n      });\n    }),\n  ).pipe(Layer.provideMerge(TodoRepoTestRef.layer));\n}\n\n\u002F\u002F In test: access raw store\nconst todos = yield * Ref.get(yield * TodoRepoTestRef);\n```\n\n## Mocking LanguageModel (AI SDK)\n\nThe AI SDK uses `Context.Service` exactly like any other service:\n\n```typescript\nimport { LanguageModel } from \"effect\u002Funstable\u002Fai\";\n\nconst mockLmLayer = (responses: ReadonlyArray\u003Cstring>) =>\n  Layer.effect(\n    LanguageModel.LanguageModel,\n    Effect.gen(function* () {\n      const idx = yield* Ref.make(0);\n      return {\n        generateText: Effect.fnUntraced(function* (_options) {\n          const i = yield* Ref.getAndUpdate(idx, (n) => n + 1);\n          return {\n            text: responses[Math.min(i, responses.length - 1)] ?? \"\",\n            usage: { inputTokens: 0, outputTokens: 0 },\n            toolCalls: [],\n            finishReason: \"stop\" as const,\n          };\n        }),\n        generateObject: () => Effect.die(new Error(\"not mocked\")),\n        streamText: () => Effect.die(new Error(\"not mocked\")),\n      } as unknown as LanguageModel.Service;\n    }),\n  );\n```\n\n## Per-test provide — isolated\n\n```typescript\ntest(\"processes item\", async () => {\n  await Effect.runPromise(\n    Effect.gen(function* () {\n      const svc = yield* MyService;\n      const result = yield* svc.doThing(\"input\");\n      expect(result).toBe(\"expected\");\n    }).pipe(Effect.provide(MyService.layerTest)),\n  );\n});\n```\n\n## Shared layer across describe block — use `layer()` from @effect\u002Fvitest\n\n```typescript\nimport { layer } from \"@effect\u002Fvitest\";\n\nlayer(TodoRepo.layerTest)(\"TodoRepo tests\", (it) => {\n  it.effect(\"creates a todo\", () =>\n    Effect.gen(function* () {\n      const repo = yield* TodoRepo;\n      yield* repo.create(\"Write tests\");\n      const all = yield* repo.list;\n      expect(all.length).toBeGreaterThanOrEqual(1);\n    }),\n  );\n});\n```\n\n## Counting calls with Ref\n\n```typescript\ntest(\"calls LLM twice for maxDepth=0\", async () => {\n  await Effect.runPromise(\n    Effect.gen(function* () {\n      const callCount = yield* Ref.make(0);\n      const layer = mockLmLayerCounting(callCount);\n      yield* myEffect.pipe(Effect.provide(layer));\n      const total = yield* Ref.get(callCount);\n      expect(total).toBeGreaterThanOrEqual(2);\n    }),\n  );\n});\n```\n",{"name":5,"license":14,"description":7,"compatibility":32},"main","816c073aaf171300af3605b06f7ec5aa7370ef0b","reference\u002Fghui\u002F.opencode\u002Fskills\u002Feffect-ts\u002Ftesting\u002F01-test-services","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro","https:\u002F\u002Fgithub.com\u002Flambda-solver","2026-05-31 17:08:46",{"source":8,"skillName":5,"commands":48,"prompt":58,"zipAvailable":59},[49,52,55],{"tool":50,"command":51},"npx","npx skills add lambda-solver\u002Flambdabench-pro --skill 01-test-services",{"tool":53,"command":54},"bunx","bunx skills add lambda-solver\u002Flambdabench-pro --skill 01-test-services",{"tool":56,"command":57},"pnpm","pnpm dlx skills add lambda-solver\u002Flambdabench-pro --skill 01-test-services","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Feffect-ts\u002Ftesting\u002F01-test-services\u002FSKILL.md",true,[],[62,90,112,131,152,170],{"skillNo":63,"name":64,"nameZh":6,"description":65,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":66,"categories":67,"keywords":71,"publishedAt":36},"01M71H9FNA5D6Y4ANXD18Z1Z3X","tmux-workflow","Development workflow with tmux — run dev servers, Storybook, and background processes in persistent sessions with live logs, while keeping OpenCode free for chat","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Ftmux\u002FSKILL.md",[68],{"categoryNo":69,"categoryName":70,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[72,73,74,75,76,77,78,79,80,81,82,32,31,83,84,85,86,87,88,89],"tmux","workflow","tmux workflow","chat","dev servers","background","processes","storybook","live","servers","run","logs","development workflow","free","dev","sessions","for","development",{"skillNo":91,"name":92,"nameZh":6,"description":93,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":94,"categories":95,"keywords":99,"publishedAt":36},"01M71H9DQJDAJVZX37NDN99D3P","04-anti-patterns","React anti-patterns to avoid — manual JSX repetition, useEffect misuse, reference instability, prop drilling, conditional hooks, and common performance traps","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fpatterns\u002F04-anti-patterns\u002FSKILL.md",[96],{"categoryNo":97,"categoryName":98,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",[100,101,102,103,104,105,106,107,108,109,110,32,111],"patterns","anti patterns","04 anti patterns","common","jsx","reference","conditional","hooks","manual","performance","anti-patterns","react",{"skillNo":113,"name":114,"nameZh":6,"description":115,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":116,"categories":117,"keywords":119,"publishedAt":36},"01M71H9BXF511W2R3B73S5P9ZN","03-state-management","React state management patterns — lifting state, colocation, URL state, server state with TanStack Query, local state with useState\u002FuseReducer","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fpatterns\u002F03-state-management\u002FSKILL.md",[118],{"categoryNo":97,"categoryName":98,"taxonomyType":20,"taxonomyTypeDesc":21},[120,121,122,123,111,124,100,125,126,127,128,32,129,31,130],"management","state management","state","03 state management","query","URL","tanstack","react state management","local","server","tanstack query",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":135,"categories":136,"keywords":138,"publishedAt":36},"01M71H9AAGVWMYQ4NG82QPYCTE","02-component-composition","React component composition patterns — render props, compound components, list rendering with map, slot patterns, and layout primitives","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fpatterns\u002F02-component-composition\u002FSKILL.md",[137],{"categoryNo":97,"categoryName":98,"taxonomyType":20,"taxonomyTypeDesc":21},[139,140,141,32,31,142,143,111,144,100,145,146,147,148,149,150,151],"02 component composition","component","Composition","compound","components","primitives","composition patterns","list","map","rendering","render","react component","layout",{"skillNo":153,"name":154,"nameZh":6,"description":155,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":156,"categories":157,"keywords":159,"publishedAt":36},"01M71H98RDXRQZDR496SRD906M","01-fp-style","React functional programming style — pure components, hooks conventions, Effect Atom integration, and Tailwind patterns","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fpatterns\u002F01-fp-style\u002FSKILL.md",[158],{"categoryNo":97,"categoryName":98,"taxonomyType":20,"taxonomyTypeDesc":21},[160,161,162,100,163,164,32,165,111,166,143,107,167,168,169,26],"01 fp style","style","atom","conventions","integration","functional","programming","Tailwind","tailwind patterns","functional programming",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":177,"publishedAt":36},"01M71H95PYGAWBS5C8KN0C4WGT","react-animation","React animation with react\u002Fmotion (Framer Motion) — declarative animations, gestures, layout animations, and performance best practices","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fanimation\u002FSKILL.md",[176],{"categoryNo":97,"categoryName":98,"taxonomyType":20,"taxonomyTypeDesc":21},[111,178,179,109,180,32,151,181,182,31,183,184,185,186],"react animation","Animation","framer motion","motion","best practices","framer","gestures","animations","declarative"]