ExUnit 测试框架
ExUnit 是 Elixir 内置的测试框架,无需额外安装:
# test/blog/posts_test.exs
defmodule Blog.PostsTest do
use Blog.DataCase # Phoenix 提供,带数据库沙箱
alias Blog.Posts
describe "create_post/1" do
test "有效参数时成功创建" do
user = insert(:user) # ExMachina 工厂
attrs = %{title: "测试文章", body: "这是一篇测试文章内容", author_id: user.id}
assert {:ok, post} = Posts.create_post(attrs)
assert post.title == "测试文章"
assert post.published == false
end
test "标题为空时返回错误" do
attrs = %{title: "", body: "内容"}
assert {:error, changeset} = Posts.create_post(attrs)
assert "can't be blank" in errors_on(changeset).title
end
test "标题过短时返回错误" do
attrs = %{title: "短", body: "内容足够长的正文"}
assert {:error, changeset} = Posts.create_post(attrs)
assert "should be at least 5 character(s)" in errors_on(changeset).title
end
end
end
测试 GenServer
defmodule Blog.ShoppingCartTest do
use ExUnit.Case, async: true # async: true 并发执行测试
setup do
{:ok, cart} = ShoppingCart.start_link()
%{cart: cart}
end
test "初始购物车为空", %{cart: cart} do
assert ShoppingCart.get_cart(cart) == []
end
test "添加商品后总价正确", %{cart: cart} do
ShoppingCart.add_item(cart, %{id: 1, name: "键盘", price: 299})
ShoppingCart.add_item(cart, %{id: 2, name: "鼠标", price: 199})
assert ShoppingCart.total(cart) == 498
end
end
测试 LiveView
defmodule BlogWeb.CounterLiveTest do
use BlogWeb.ConnCase
import Phoenix.LiveViewTest
test "初始显示 0", %{conn: conn} do
{:ok, view, html} = live(conn, "/counter")
assert html =~ "计数器:0"
end
test "点击增加后计数器 +1", %{conn: conn} do
{:ok, view, _html} = live(conn, "/counter")
html = view |> element("button", "+") |> render_click()
assert html =~ "计数器:1"
end
end
ExMachina:测试数据工厂
# test/support/factory.ex
defmodule Blog.Factory do
use ExMachina.Ecto, repo: Blog.Repo
def user_factory do
%Blog.Accounts.User{
email: sequence(:email, &"user#{&1}@example.com"),
username: sequence(:username, &"user#{&1}"),
hashed_password: Bcrypt.hash_pwd_salt("password123")
}
end
def post_factory do
%Blog.Posts.Post{
title: sequence(:title, &"测试文章 #{&1}"),
body: "这是一段足够长的文章正文内容,用于测试验证。",
published: false,
author: build(:user)
}
end
end
# 在测试中使用
user = insert(:user)
post = insert(:post, author: user, published: true)
posts = insert_list(5, :post) # 批量创建 5 篇
mix release:自包含二进制发布
# 生产环境构建(包含 Erlang 运行时,无需目标机器安装 Elixir)
MIX_ENV=prod mix do deps.get, assets.deploy, release
# 发布包位于 _build/prod/rel/blog/
# 启动(前台)
_build/prod/rel/blog/bin/blog start
# 守护进程方式
_build/prod/rel/blog/bin/blog daemon
# 连接到运行中的节点(热调试!)
_build/prod/rel/blog/bin/blog remote
# 运行迁移
_build/prod/rel/blog/bin/blog eval "Blog.Release.migrate()"
Docker 化部署
# Dockerfile(Phoenix 官方推荐多阶段构建)
FROM hexpm/elixir:1.17.0-erlang-27.0-debian-bookworm-20240701-slim AS build
WORKDIR /app
RUN apt-get update -y && apt-get install -y build-essential git nodejs npm
COPY mix.exs mix.lock ./
COPY config config
RUN mix deps.get --only prod
RUN MIX_ENV=prod mix deps.compile
COPY assets assets
COPY priv priv
COPY lib lib
RUN MIX_ENV=prod mix assets.deploy
RUN MIX_ENV=prod mix release
# 最终运行镜像(极小,仅含运行时)
FROM debian:bookworm-20240701-slim AS app
RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=build /app/_build/prod/rel/blog ./
EXPOSE 4000
ENV PHX_SERVER=true
ENTRYPOINT ["/app/bin/blog", "start"]
Fly.io 部署(Elixir 官方推荐)
Fly.io 对 Elixir/Phoenix 有原生支持,自动识别项目并生成配置:
# 安装 flyctl
curl -L https://fly.io/install.sh | sh
# 初始化(自动检测 Phoenix,生成 fly.toml 和 Dockerfile)
fly launch
# 设置数据库(Postgres)
fly postgres create --name blog-db
fly postgres attach blog-db
# 部署
fly deploy
# 查看日志
fly logs
# 进入生产 REPL(这是 Elixir 的独特优势!)
fly ssh console -C "/app/bin/blog remote"
# iex> :observer.start() 实时查看进程树!
Elixir 生产调试的超能力:fly ssh console 连接到生产节点后,你可以运行任意 Elixir 代码、查看进程状态、修改运行中的状态——完全无需重启。这是 BEAM 热代码更新与进程透明性带来的独特体验,其他语言运行时几乎无法实现。
Elixir 生态与学习资源
核心生态库
| 库名 | 用途 | 类比 |
|---|---|---|
| Ecto | 数据库 ORM | ActiveRecord / Prisma |
| Phoenix | Web 框架 | Rails / Django |
| LiveView | 实时 UI | React + WebSocket |
| Oban | 后台任务队列 | Sidekiq / BullMQ |
| Absinthe | GraphQL 服务端 | graphql-ruby |
| Tesla | HTTP 客户端 | axios / Got |
| ExDoc | 文档生成 | JSDoc / Sphinx |
| Credo | 代码分析(Lint) | ESLint |
| Dialyxir | 类型检查(Dialyzer) | TypeScript |
| Broadway | 数据处理管道 | Apache Kafka Streams |
学习资源推荐
- 官方文档 hexdocs.pm/elixir — Elixir 核心文档,质量极高,每个函数都有示例。hexdocs.pm/phoenix — Phoenix 框架文档,含 LiveView 完整指南。
- 书籍 Programming Elixir(Dave Thomas) — 入门必读,作者是 Ruby 社区传奇人物。Elixir in Action(Saša Juric) — 深入 OTP 并发,工程实践必备。Programming Phoenix LiveView — LiveView 专著,官方推荐。
- 社区 elixirforum.com — 官方论坛,问题回复极快。ElixirConf — 年度大会,YouTube 有历年视频存档。Thinking Elixir — 播客,追踪生态最新动态。
- 练习 exercism.io/tracks/elixir — 交互式练习题,有导师指导。advent of code — 年度编程挑战,Elixir 社区每年都有大量参与者。
Phoenix LiveView 未来展望
LiveView 正在不断演进,几个值得关注的方向:
- LiveView Native:将 LiveView 模式扩展到原生 iOS/Android,服务端驱动移动端 UI
- LiveView 1.0(已发布):API 稳定,生产就绪
- Ash Framework:声明式资源框架,大幅简化 Context/Ecto 样板代码
- Nx / Bumblebee:Elixir 的机器学习生态,在 BEAM 上运行神经网络
- Membrane Framework:Elixir 多媒体处理框架,音视频流处理
课程总结:恭喜你完成了 Elixir / Phoenix LiveView 全部 10 章!你已掌握:BEAM VM 并发模型、Elixir 函数式语法、OTP 容错框架、Phoenix MVC、LiveView 实时 UI、Channels 广播、Ecto 数据建模、测试与 Fly.io 生产部署。Elixir 是一门小众但极具深度的语言,学习它不只是多一项技能,更是重新理解并发与可靠系统的一次思维升级。