Elixir Cheatsheet

Mix, Testing, and Tooling

Use this Elixir reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Mix Project Commands

mix new demo               # library/app skeleton (--sup for a supervision tree)
mix compile
mix test
mix format
mix deps.get
mix deps.update package_name
mix hex.info package_name
mix run -e 'IO.puts(Demo.hello())'
mix xref graph --label compile-connected   # find compile-time dependencies

Mix is the build tool: it compiles code, manages dependencies, runs tests, formats files, and hosts custom tasks (lib/mix/tasks/*.ex).

mix.exs Essentials

def project do
  [
    app: :demo,
    version: "0.1.0",
    elixir: "~> 1.18",
    deps: deps()
  ]
end

def application do
  [extra_applications: [:logger], mod: {Demo.Application, []}]
end

defp deps do
  [
    {:jason, "~> 1.4"},
    {:req, "~> 0.5"},
    {:credo, "~> 1.7", only: [:dev, :test], runtime: false}
  ]
end

elixir: "~> 1.18" is a minimum requirement, not a pin. mix.lock records resolved dependency versions — commit it for applications.

ExUnit Basics

# test/demo_test.exs
defmodule DemoTest do
  use ExUnit.Case, async: true

  test "adds numbers" do
    assert Demo.add(2, 3) == 5
  end
end

Use async: true for tests that do not share global state, databases, files, or named processes. test/test_helper.exs runs first and calls ExUnit.start().

describe, setup, and Tags

defmodule AccountsTest do
  use ExUnit.Case, async: true

  setup_all do
    {:ok, conn: connect()}          # once per module; merged into context
  end

  setup context do
    user = create_user()
    on_exit(fn -> cleanup(user) end)
    {:ok, user: user}               # before every test
  end

  describe "create_user/1" do
    test "with valid attrs", %{user: user, conn: conn} do
      assert user.id
    end

    @tag :slow
    test "bulk import" do
      # ...
    end
  end
end
mix test test/accounts_test.exs        # one file
mix test test/accounts_test.exs:42     # the test at line 42
mix test --only slow                   # only @tag :slow tests
mix test --exclude slow                # skip them (or ExUnit.configure(exclude: [:slow]))
mix test --failed                      # re-run last failures
mix test --stale                       # only tests affected by changed code

Assertions

assert value == 3
refute Enum.empty?(items)
assert {:ok, user} = Accounts.create_user(attrs)     # pattern-match assertion
assert_raise ArgumentError, fn -> bad_call() end
assert_receive {:done, _}, 500                        # message-passing tests
assert_in_delta 0.1 + 0.2, 0.3, 1.0e-9

Pattern matching inside assert is idiomatic for tagged results, and failures print a diff of the match.

Doctests

defmodule MathBox do
  @doc """
      iex> MathBox.double(4)
      8
  """
  def double(n), do: n * 2
end
defmodule MathBoxTest do
  use ExUnit.Case, async: true
  doctest MathBox
end

Doctests keep examples executable. Use them for small deterministic functions, not complex setup.

Formatting, Credo, Dialyzer

mix format                    # standard formatter (config: .formatter.exs)
mix format --check-formatted  # CI check
mix credo --strict            # style/design lints
mix dialyzer                  # success typing (dep: dialyxir)
mix test --cover              # coverage report

IEx Debugging

dbg(value)              # prints code + value; pipes show each step
IO.inspect(value, label: "value")
require IEx; IEx.pry()  # breakpoint (run under iex -S mix)
iex -S mix              # project shell; recompile with `recompile`
iex -S mix test         # pry inside tests needs --trace or a timeout bump
h Enum.map              # docs in IEx; i value — type info

Application Config

# config/config.exs — compile time
config :demo, feature_flags: [beta: true]

# config/runtime.exs — boot time, right place for env vars
config :demo, Demo.Repo, url: System.fetch_env!("DATABASE_URL")

# reading
Application.fetch_env!(:demo, :feature_flags)
Application.get_env(:demo, :missing, :default)

Runtime secrets come from environment variables or a secret manager via config/runtime.exs, never committed config files.