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
No Comments