Elixir Fundamentals: Functional Programming and OTP · Processes, OTP and Fault Tolerance
Mix, Testing and the Phoenix Ecosystem
Create projects with Mix, manage dependencies from Hex, write tests with ExUnit and doctests, and see where Phoenix, LiveView, Ecto and Oban fit when you build real web apps.
What you'll build
A tested module with ExUnit-style tests and a doctest, plus a map of the tools you'll use when you move from scripts to a real Phoenix application.
The idea
Mix is Elixir's build tool:
mix new inventory --sup # a project with a supervision tree
mix deps.get # fetch dependencies listed in mix.exs (from hex.pm)
mix test # run ExUnit tests
mix format # format code
iex -S mix # an interactive shell with your project loaded
ExUnit is the built-in test framework: test "description" do assert ... end, with refute, assert_raise, setup blocks, and async: true to run test modules in parallel. Doctests run the iex> examples in your documentation as tests, so docs never go stale.
The ecosystem for web apps:
| Tool | What it does |
|---|---|
| Phoenix | Web framework: router, controllers, JSON APIs, channels (WebSockets), PubSub |
| LiveView | Server-rendered interactive UIs over a WebSocket, without writing a JS frontend |
| Ecto | Database wrapper and query language: schemas, changesets (validation), migrations |
| Oban | Background jobs stored in PostgreSQL, with retries and scheduling |
| Req | A friendly HTTP client |
The code
This runnable script uses ExUnit directly. In a project, tests live in test/*_test.exs, and doctest Inventory also runs the iex> examples from the docs (doctests need the compiled module, so they run under mix test, not in a plain script):
ExUnit.start(autorun: false, seed: 0, formatters: []) # results are printed below
defmodule Inventory do
@moduledoc "Stock levels for a small shop."
@doc """
Removes `qty` items from stock.
iex> Inventory.take(%{apples: 5}, :apples, 2)
{:ok, %{apples: 3}}
iex> Inventory.take(%{apples: 1}, :apples, 2)
{:error, :insufficient_stock}
"""
def take(stock, item, qty) when qty > 0 do
case Map.get(stock, item, 0) do
available when available >= qty -> {:ok, Map.put(stock, item, available - qty)}
_ -> {:error, :insufficient_stock}
end
end
def take(_stock, _item, _qty), do: {:error, :invalid_quantity}
end
defmodule InventoryTest do
use ExUnit.Case, async: true
# In a Mix project, `doctest Inventory` here would also run the iex> examples above.
setup do
{:ok, stock: %{apples: 5, pears: 0}}
end
test "takes items that are in stock", %{stock: stock} do
assert {:ok, %{apples: 2}} = Inventory.take(stock, :apples, 3)
end
test "refuses to go below zero", %{stock: stock} do
assert Inventory.take(stock, :pears, 1) == {:error, :insufficient_stock}
end
test "rejects zero and negative quantities", %{stock: stock} do
assert Inventory.take(stock, :apples, 0) == {:error, :invalid_quantity}
refute match?({:ok, _}, Inventory.take(stock, :apples, -2))
end
end
result = ExUnit.run()
IO.puts("#{result.total} tests, #{result.failures} failures")
Output
3 tests, 0 failures
Run this on your computer — see “Get Started” for setup.
Common mistakes
- Untested GenServers. Test the pure functions behind them separately; keep callbacks thin.
async: truewith shared global state (named processes, the database without sandboxing), which makes tests flaky.- Skipping changesets in Ecto and validating by hand; changesets centralise casting and validation.
- Starting with microservices. A single Phoenix app with contexts scales a long way on the BEAM.
Key points
- Mix creates, builds, tests and formats projects; Hex hosts packages.
- ExUnit tests with
assert/refute,setup,async: true; doctests keep examples honest. - Phoenix (web), LiveView (interactive UIs), Ecto (database), Oban (jobs).
- Keep business logic in plain modules; processes and web layers stay thin.
Further reading: Introduction to Mix, ExUnit, Phoenix.
Practice questions
What are doctests in Elixir?
Answer
Examples written in a function's @doc as iex> prompts and expected results. Adding doctest Module to an ExUnit test module runs them as tests, so documentation examples stay correct.
What are Phoenix, LiveView and Ecto?
Answer
Phoenix is Elixir's web framework (routing, controllers, channels, PubSub); LiveView builds interactive, real-time UIs rendered on the server over a WebSocket; Ecto is the database library (schemas, changesets for validation, queries, migrations).
Open in the app to mark lessons complete, take notes and track your progress.