name: 测试 description: Elixir 测试模式 - ExUnit、Mox、属性测试、LiveView 测试。在编写或审查测试时加载。 user-invocable: false
Elixir 测试参考
Elixir 测试模式的快速参考。
铁律 — 永不违反这些
- 默认异步 — 除非测试修改全局状态,否则使用
async: true - 沙盒隔离 — 所有数据库测试使用 Ecto.Adapters.SQL.Sandbox
- 仅在边界模拟 — 永远不要模拟数据库、内部模块或标准库
- 行为作为契约 — 所有模拟必须实现定义的
@callback行为 - 默认构建 — 在工厂中使用
build/2;仅在需要数据库 ID、约束或持久化关联时使用insert/2 - 不使用 Process.sleep — 对于异步操作,使用带超时的
assert_receive - VERIFY_ON_EXIT! — 在 Mox 测试设置中始终调用
- 工厂匹配架构必需字段 — 工厂定义必须包括所有在架构变化集中有
validate_required的字段。缺失字段会导致级联测试失败
快速决策
使用哪个测试用例?
| 测试类型 | 使用 |
|---|---|
| 控制器/API | use MyAppWeb.ConnCase |
| 上下文/架构 | use MyApp.DataCase |
| LiveView | use MyAppWeb.ConnCase + import Phoenix.LiveViewTest |
| 纯逻辑 | use ExUnit.Case, async: true |
何时使用 async: true?
- ✅ 纯函数,无共享状态
- ✅ 使用沙盒的数据库测试(PostgreSQL)
- ❌ 修改
Application.put_env的测试 - ❌ 使用 Mox 全局模式的测试
模拟与否?
- ✅ 模拟:外部 API、电子邮件服务、文件存储
- ❌ 不模拟:数据库、内部模块、标准库
build() 或 insert()?
- 默认使用
build()以提高速度 - 仅在需要数据库 ID、约束或持久化关联时使用
insert()
快速模式
# 设置链
setup [:create_user, :authenticate]
# 模式匹配断言
assert {:ok, %User{name: name}} = create_user(attrs)
# 异步消息断言
assert_receive {:user_created, _}, 5000
# Mox 设置
setup :verify_on_exit!
expect(MockAPI, :call, fn _ -> {:ok, "data"} end)
# LiveView 异步
html = render_async(view) # 必须调用以用于 assign_async
常见反模式
| 错误 | 正确 |
|---|---|
Process.sleep(100) |
assert_receive {:done, _}, 5000 |
在工厂中使用 insert(:user) |
在工厂中使用 build(:user) |
async: true 与 set_mox_global() |
async: false |
| 模拟内部模块 | 通过公共 API 测试 |
参考
有关详细模式,请参阅:
references/exunit-patterns.md- 设置、断言、标签references/mox-patterns.md- 行为、expect/stub、异步references/liveview-testing.md- 表单、异步、上传references/factory-patterns.md- ExMachina、序列、特征