Elixir单元测试 #
一、ExUnit概述 #
1.1 什么是ExUnit #
ExUnit是Elixir内置的测试框架,提供:
- 测试组织
- 断言函数
- 测试辅助
- 测试报告
1.2 测试结构 #
text
test/
├── test_helper.exs
├── my_app_test.exs
└── my_app/
├── user_test.exs
└── post_test.exs
二、基本测试 #
2.1 测试定义 #
elixir
defmodule MyApp.UserTest do
use ExUnit.Case
test "greets the world" do
assert 1 + 1 == 2
end
test "name is not empty" do
user = %User{name: "Alice"}
assert user.name != ""
end
end
2.2 运行测试 #
bash
mix test
mix test test/my_app/user_test.exs
mix test test/my_app/user_test.exs:10
三、断言 #
3.1 基本断言 #
elixir
assert 1 + 1 == 2
refute 1 + 1 == 3
assert true
refute false
3.2 异常断言 #
elixir
assert_raise ArithmeticError, fn ->
1 / 0
end
assert_raise RuntimeError, "error message", fn ->
raise "error message"
end
3.3 模式匹配断言 #
elixir
assert {:ok, result} = some_function()
assert %User{name: "Alice"} = user
3.4 近似断言 #
elixir
assert_in_delta 1.1, 1.2, 0.2
四、测试组织 #
4.1 describe #
elixir
defmodule MyApp.UserTest do
use ExUnit.Case
describe "create/1" do
test "creates a user with valid params" do
assert {:ok, _user} = Accounts.create_user(%{name: "Alice"})
end
test "returns error with invalid params" do
assert {:error, _changeset} = Accounts.create_user(%{})
end
end
describe "update/2" do
test "updates user name" do
user = user_fixture()
assert {:ok, user} = Accounts.update_user(user, %{name: "Bob"})
assert user.name == "Bob"
end
end
end
4.2 setup #
elixir
defmodule MyApp.UserTest do
use ExUnit.Case
setup do
user = %User{name: "Alice"}
{:ok, user: user}
end
test "user has name", %{user: user} do
assert user.name == "Alice"
end
end
4.3 setup_all #
elixir
setup_all do
Application.ensure_all_started(:my_app)
:ok
end
4.4 on_exit #
elixir
setup do
pid = start_server()
on_exit(fn -> stop_server(pid) end)
{:ok, server: pid}
end
五、测试标签 #
5.1 标记测试 #
elixir
@tag :slow
test "slow operation" do
:timer.sleep(1000)
assert true
end
@tag :wip
test "work in progress" do
assert false
end
5.2 过滤测试 #
bash
mix test --exclude slow
mix test --only slow
mix test --only wip
5.3 配置默认标签 #
elixir
ExUnit.start(exclude: [:slow, :wip])
六、测试辅助 #
6.1 测试夹具 #
elixir
defmodule MyApp.Fixtures do
def user_fixture(attrs \\ %{}) do
{:ok, user} = MyApp.Accounts.create_user(Map.merge(%{
name: "Alice",
email: "alice@example.com"
}, attrs))
user
end
end
6.2 使用夹具 #
elixir
defmodule MyApp.UserTest do
use ExUnit.Case
import MyApp.Fixtures
test "create user" do
user = user_fixture()
assert user.name == "Alice"
end
end
七、Mock #
7.1 使用mox #
elixir
defmodule MyApp.HTTPClient do
@callback get(String.t()) :: {:ok, String.t()} | {:error, term()}
end
defmodule MyApp.HTTPClientMock do
@behaviour MyApp.HTTPClient
def get(_url), do: {:ok, "response"}
end
7.2 配置mox #
elixir
Mox.defmock(MyApp.HTTPClientMock, for: MyApp.HTTPClient)
Application.put_env(:my_app, :http_client, MyApp.HTTPClientMock)
7.3 使用Mock #
elixir
import Mox
setup :verify_on_exit!
test "fetches data" do
expect(MyApp.HTTPClientMock, :get, fn "http://example.com" ->
{:ok, "data"}
end)
assert {:ok, "data"} = MyApp.Service.fetch()
end
八、测试GenServer #
8.1 测试GenServer #
elixir
defmodule MyApp.CounterTest do
use ExUnit.Case
setup do
{:ok, pid} = Counter.start_link(0)
{:ok, counter: pid}
end
test "increments counter", %{counter: counter} do
Counter.increment(counter)
assert Counter.get(counter) == 1
end
end
九、总结 #
本章学习了:
| 特性 | 用途 |
|---|---|
| test | 定义测试 |
| assert | 断言 |
| describe | 组织测试 |
| setup | 测试准备 |
| @tag | 测试标签 |
准备好学习测试进阶了吗?让我们进入下一章。
最后更新:2026-03-27