This is part one of a series on Elixir Testing.

In this episode we'll use the built-in library ExUnit to TDD our way through building a module to calculate Fibonacci numbers. We'll have two primary functions— Fibonacci.nth/1 which will return the nth number of Fibonacci sequence, and Fibonacci.first_n/1 which will return a list of the the first n numbers of the sequence.

Since the goal is to use TDD, we'll stick to a red -> green -> refactor workflow, even when optimizing for performance!

The end state for our test module:

This is what our tests look like when we're done:

defmodule FibonacciTest do use ExUnit.Case doctest Fibonacci @moduletag timeout: 1000 describe "calculate the nth fibonacci number" do test "the first number is 1" do assert Fibonacci.nth(1) == 1 end test "the second number is 1" do assert Fibonacci.nth(2) == 1 end test "the third number is 2" do assert Fibonacci.nth(3) == 2 end test "the fourth number is 3" do assert Fibonacci.nth(4) == 3 end test "the fifth number is 5" do assert Fibonacci.nth(5) == 5 end test "the sixth number is 8" do assert Fibonacci.nth(6) == 8 end test "shouldn't take too long" do assert Fibonacci.nth(100) == Fibonacci.nth(99) + Fibonacci.nth(98) end end describe "calculate the first n fibonacci numbers" do test "doesn't return any for zero" do assert Fibonacci.first_n(0) == [] end test "returns the first number for one" do assert Fibonacci.first_n(1) == [1] end test "two returns the first two numbers" do assert Fibonacci.first_n(2) == [1, 1] end test "6 returns the first 6 numbers" do assert Fibonacci.first_n(6) == [1, 1, 2, 3, 5, 8] end test "10000 isn't too slow" do first10k = Fibonacci.first_n(10000) assert Fibonacci.first_n(10001) == first10k ++ [Fibonacci.nth(10001)] end end end

The end state for our Fibonacci module:

defmodule Fibonacci do @moduledoc """ Documentation for Fibonacci. """ @doc """ Calculates numbers from the Fibonacci sequence ## Examples iex> Fibonacci.nth(1) 1 """ def nth(n), do: nth(n - 1, 0, 1) def nth(0, _acc1, acc2), do: acc2 def nth(n, acc1, acc2), do: nth(n - 1, acc2, acc1 + acc2) def first_n(n), do: first_n(n, []) def first_n(0, acc), do: Enum.reverse(acc) def first_n(n, []), do: first_n(n - 1, [1]) def first_n(n, [1]), do: first_n(n - 1, [1, 1]) def first_n(n, [e1 | [e2 | _tail]] = acc) do first_n(n - 1, [e1 + e2 | acc]) end end

Next episode: Fixing generated Phoenix tests