一行use解决Hammox.Protect宏如何让Elixir测试模块自带契约检查【免费下载链接】hammox automated contract testing via type checking for Elixir functions and mocks项目地址: https://gitcode.com/gh_mirrors/ha/hammox还在Elixir测试里手写setup_all、再一个个从测试上下文里掏匿名函数吗Elixir契约测试库Hammox给出了更简单的答案只需在测试模块顶部加一行use Hammox.Protect它就自动为你被测模块的每个回调函数生成带契约检查的版本——调用时自动按 behaviour 的 typespec 声明校验参数与返回值违规立即抛出清晰的Hammox.TypeMatchError。Hammox.Protect 三步快速上手一行 use 开启契约检查对新手来说理解契约检查只要一句话你的函数必须遵守 behaviour 里用callback写下的合同参数类型、返回类型都不许错。Hammox.Protect 就是把这份合同变成运行时自动执行的法律。第 1 步添加依赖在mix.exs中声明依赖仅测试环境使用def deps do [{:hammox, ~ 0.7, only: :test}] end第 2 步在测试模块加一行 use假设RealDatabase实现了Database这个 behaviourdefmodule RealDatabaseTest do use ExUnit.Case, async: true use Hammox.Protect, module: RealDatabase, behaviour: Database test get_users/0 返回值符合契约 do assert {:ok, [real-joe]} get_users() end end第 3 步像普通函数一样调用注意测试里直接写get_users()而不是RealDatabase.get_users()。效果类似import但每个函数都被自动包裹了一层类型检查。 就这么多。不需要 setup、不需要匿名函数、不需要测试上下文传递。Protect 宏的工作原理自动收集回调并注入类型校验这一行的魔法藏在哪宏的实现位于lib/hammox/protect.ex流程非常直白解析选项从:module、:behaviour、:funs中确定要保护哪些函数收集回调通过Code.Typespec.fetch_callbacks/1读取 behaviour 的全部callback声明生成本地函数为每个回调在你的测试模块中定义一个同名函数内部先调用原函数再用 Hammox 的类型引擎lib/hammox/type_engine.ex校验参数和返回值违规即抛出类型不匹配时抛出Hammox.TypeMatchError定义见lib/hammox/type_match_error.ex由于检查发生在运行时那些 Dialyzer 静态分析够不着的 mock 数据、边界返回值都能被抓住——这正是 Hammox 相比静态类型检查的核心价值。Hammox.Protect 三个选项速查module、behaviour 与 funs选项必填说明:module✅ 是要实现契约的实现模块通常就是被测模块:behaviour可选定义契约的 behaviour省略时默认用:module本身声明的回调:funs可选明确列出要保护的函数如[foo: 0, bar: 1]当回调和实现写在同一个模块时Elixir 常见写法只需:module一个选项defmodule Calculator do callback add(integer(), integer()) :: integer() def add(a, b), do: a b end # 测试里只写一行 use Hammox.Protect, module: Calculator一个实现模块对应多个 behaviour时可以写多组behaviour/funs。注意每组:funs只作用于紧跟在它前面的那个:behaviour省略:funs则保护该 behaviour 的全部回调use Hammox.Protect, module: MyApp.Service, behaviour: MyApp.Cache, funs: [get: 1], # 只保护 Cache 的 get/1 behaviour: MyApp.Logger # 未给 funs保护 Logger 全部回调官方测试用例中的真实用法可以参考test/hammox/protect_test.exs覆盖了单模块、多 behaviour 等全部场景。契约被打破时Hammox.TypeMatchError 长什么样当函数返回值不符合callback声明时测试会立刻失败报错信息直指问题核心** (Hammox.TypeMatchError) Returned value [joe, jim] does not match type {:ok, [binary()]} | {:error, term()}.典型的翻车场景behaviour 升级为返回{:ok, ...}元组后旧的 mock 或旧实现仍返回裸值——测试表面通过、生产必炸。有了契约检查这类问题在测试阶段就无处遁形。Hammox.Protect 与显式 Hammox.protect 对比怎么选Hammox 本身lib/hammox.ex提供了显式的Hammox.protect/3系列 API返回匿名函数配合setup_all使用。两者对比一目了然维度use Hammox.Protect宏Hammox.protect/3显式代码量一行最简洁需 setup 上下文传参调用方式get_users()直接调用get_users_0.()匿名函数灵活度编译期确定全局生效运行时确定可按测试定制适合场景测试整个 behaviour 的常规实现只保护个别函数、动态组合经验法则日常测试优先用宏需要精细控制比如某条测试只想部分保护时再退回显式 API。Hammox 对 Mox 完全兼容两者可以混用。常见坑与报错信息速查⚠️ 编译期就可能遇到的两个报错都来自Hammox.Protect.extract_opts!/1的参数校验忘记:module→Please specify :module to protect with Hammox.Protect.模块没有任何回调普通模块或空的 behaviour→The module X does not contain any callbacks. Please use a behaviour with at least one callback.另外两个运行时的注意点函数必须有 typespecbehaviour 中找不到对应callback时会抛出TypespecNotFoundError检查模块名和函数名、参数数量arity是否写对匿名函数类型只校验 aritytypespec 里声明的函数类型参数只检查参数个数不检查参数和返回的具体类型小结use Hammox.Protect用一行代码换来了完整的运行时契约保障自动收集 behaviour 回调、自动生成受保护的本地函数、类型不匹配立即报错。对新手而言它把测试实现是否遵守 behaviour 契约这件容易遗漏的事变成了零心智负担的默认行为。想进一步了解 Hammox 的 Telemetry 事件与可观测性可以阅读项目自带的指南guides/Telemetry.md核心宏的完整源码在lib/hammox/protect.ex建议对照本文阅读一遍你会对宏生成函数这套 Elixir 魔法有更深的体会。【免费下载链接】hammox automated contract testing via type checking for Elixir functions and mocks项目地址: https://gitcode.com/gh_mirrors/ha/hammox创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考