mcpField
E
effect-testing-mocking

effect-testing-mocking

github.com/mepuka测试与质量537 次安装44 周前更新
代码仓库

关于 effect-testing-mocking

面向 Effect 开发者的测试技能,介绍如何通过模拟服务和 Layer 编写确定性测试,并要求实现前先查阅本地 Effect 源码。

主要功能

  1. 01通过 Layer 模拟服务
  2. 02显式提供测试实现
  3. 03使用确定性测试时钟
  4. 04优先查阅本地 Effect 源码

适用场景

  1. 01在无外部 I/O 的情况下测试程序
  2. 02用模拟对象替换服务依赖
  3. 03确定性验证依赖时间的行为

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mepuka/effect-ontology --skill effect-testing-mocking

安装后在 Claude Code 中输入/effect-testing-mocking确认已加载。

详细介绍

测试与模拟

适用场景

  • 需要不依赖外部 IO 的确定性测试
  • 想通过层注入模拟服务

模拟层

const RepoTest = Layer.succeed(Repo, Repo.of({ find: () => Effect.succeed(mock) }))

提供测试实现

const result = yield* Effect.provide(program, RepoTest)

测试时钟

// Provide a deterministic Clock or use platform TestClock layer if available

指南

  • 保持测试专注且快速;通过模拟服务避免网络请求
  • 显式提供层,以满足程序要求
  • 让测试贴近真实接口;不要为了测试而改造应用代码
  • 使用 .Default 层快速接线;为每个测试添加自定义模拟层
  • 优先编写运行迅速、能快速失败的小型冒烟测试

常见问题

  • 将真实 IO 泄漏到测试中 → 导致不稳定
  • 过度模拟内部实现 → 导致测试脆弱;在服务边界进行模拟

交叉链接

  • Layers & Services,了解 DI 模式
  • Time/Logging,了解确定性时间和可观测输出

本地源码参考

重要:实现前先搜索本地 Effect 源码

完整的 Effect 源码位于 docs/effect-source/。编写 Effect 代码前,务必搜索实际实现。

关键源码文件
  • Layer:docs/effect-source/effect/src/Layer.ts
  • TestClock:docs/effect-source/effect/src/TestClock.ts
  • TestContext:docs/effect-source/effect/src/TestContext.ts
搜索示例
# Find Layer.succeed patterns for mocks
grep -F "Layer.succeed" docs/effect-source/effect/src/Layer.ts

# Study TestClock operations
grep -F "TestClock" docs/effect-source/effect/src/TestClock.ts

# Find test helpers
grep -F "TestContext" docs/effect-source/effect/src/TestContext.ts

# Look at Layer test examples
grep -F "Layer.succeed" docs/effect-source/effect/test/Layer.test.ts
工作流
  1. 确定所需的测试 API(例如 Layer.succeed、TestClock)
  2. 在 docs/effect-source/effect/src/Layer.ts 中搜索实现
  3. 研究类型和测试模式
  4. 查看测试文件中的使用示例
  5. 根据实际实现编写代码

真实源码 > 文档 > 假设

实际示例:构建全面的测试层

export const TestLayer = (input?: TestLiveInput) =>
  Effect.gen(function* () {
    const tempDir = tempy.temporaryDirectory({ prefix: 'test' })
    const cwd = (yield* setupFixtureFolder({ fixture: input?.fixture, tempDir })) ?? tempDir

    const NodeOsTest = Layer.succeed(NodeOs, new NodeOs({ homedir: cwd, arch: 'arm64', platform: 'darwin' }))
    const NodeProcessTest = Layer.succeed(NodeProcess, new NodeProcess({ cwd, platform: 'darwin', arch: 'arm64' }))

    const ToolkitsRepoTest = Layer.succeed(ComposioToolkitsRepository, new ComposioToolkitsRepository({
      getToolkits: () => Effect.succeed([]),
      getToolsAsEnums: () => Effect.succeed([])
    }))

    const layers = Layer.mergeAll(
      Console.setConsole(yield* MockConsole.effect),
      CliConfig.layer(ComposioCliConfig),
      NodeProcessTest,
      Layer.provideMerge(ComposioUserContextLive, Layer.merge(BunFileSystem.layer, NodeOsTest)),
      ToolkitsRepoTest,
      EnvLangDetector.Default,
      JsPackageManagerDetector.Default,
      BunFileSystem.layer,
      BunContext.layer,
      MockTerminal.layer,
      BunPath.layer
    )

    return layers
  }).pipe(
    Logger.withMinimumLogLevel(LogLevel.Debug),
    Effect.scoped,
    Layer.unwrapEffect,
    Layer.provide(Layer.setConfigProvider(input?.baseConfigProvider ?? ConfigProvider.fromMap(new Map([]))))
  )

参考资料

评论

支持 Markdown