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.

Create a free account to save this page, take notes and track your progress. Sign in

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 tests for a documented module
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: true with 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.

Was this page helpful?