One of the coolest feature I came across in Elixir - our code documentation becomes our unit tests. I was amazed by its simplicity when I saw it in action in sample app I was writing. I feel this is one of the most incredibly and helpful feature I saw in any modern programming language in recent times. (Python has similar functionality using Python’s doctest.)

Let me explain this by giving a quick demo using few simple examples: Math functions sum and multiply.

Lets say this is our Maths module

defmodule Maths do
  def sum(num1, num2) do
    num1 + num2
  end

  def multiply(num1, num2) do
    num1 * num2
  end
end

First, lets add simple documentation to this Maths module and its functions sum and multiply

defmodule Maths do

  @moduledoc """
    Provides functions for mathematical computations
  """

  @doc """
    Returns sum of two numbers
   """
  def sum(num1, num2) do
    num1 + num2
  end

 @doc """
   Returns multiplication of two numbers
 """
 def multiply(num1, num2) do
   num1 * num2
 end

end

To generate documentation for this module and its functions, we need to run command (on console) mix docs on our Elixir project root.

This will generate following files and outcome

  • Docs successfully generated.
  • View them at "doc/index.html".

NOTE: If you face any error while running command mix docs then verify if your project mix.exs file has hex packages ex_doc and earmark present. If not, then we will need to add following 2 lines

{:earmark, "~> 0.1", only: :dev},
{:ex_doc, "~> 0.11", only: :dev}

Opening file /doc/index.html shows following nice documentation created by Elixir out of the box. Very neat.

enter image description here

Now we will write few examples in our documentation for functions sum and multiply and they will become tests. First, lets run these two functions in our console using iex -S mix

iex(9)> value = Maths.sum(1,2)
3
iex(10)> value
3
iex(11)>


iex(11)> value = Maths.multiply(2,4)
8
iex(12)> value
8
iex(13)>

We will copy paste same examples in our documentation now (three tabs indentation inside Examples block is important below.)

  @doc """
     Returns sum of two numbers

  ## Examples
         iex> value = Maths.sum(1,2)
         iex> value
         3
  """
  def sum(num1, num2) do
    num1 + num2
  end


  @doc """
    Returns multiplication of two numbers

  ## Examples
        iex> value = Maths.multiply(2,4)
        iex> value
        8
  """
  def multiply(num1, num2) do
    num1 * num2
  end

Generate documentation again using command mix docs and it will now look nicely formatted as shown in following screenshot.

enter image description here

These documentation examples will lead to some very interesting behaviour. Testing in Elixir is first class citizen i.e. unit testing is fully featured out of the box in Elixir without a real need to add any additional plugins or libraries to write and run tests around our code. Elixir’s built-in test framework is ExUnit and it includes everything we need to thoroughly test our code. And its the ExUnit.DocTest that allows us to generate tests from the code examples.

So the real magic starts now, lets run our tests and see this in live action. Following command on console will run our example tests from documentation

mix test

This will now show outcome as

Compiling 1 file (.ex)
..

Finished in 0.03 seconds
2 tests, 0 failures

Lets attempt to make our tests fail by writing incorrect outcome for sum function i.e. lets change the Example snippet for sum to

## Examples
      iex> value = Maths.sum(1,2)
      iex> value
      5

And lets run command mix test again. This time the output is

Compiling 1 file (.ex)
.

  1) test doc at Maths.sum/2 (2) (MathsTest)
     test/maths_test.exs:3
     Doctest failed
     code: value = Maths.sum(1,2)
            value === 5
     lhs:  3
     stacktrace:
       lib/maths.ex:11: Maths (module)

Finished in 0.05 seconds
2 tests, 1 failure

So the output clearly says its expecting value 3 whereas it got value as 5. Lets fix it and run again.

## Examples
      iex> value = Maths.sum(1,2)
      iex> value
      3

And lets run command mix test again. This time there will be 0 failures.

Compiling 1 file (.ex)
..

Finished in 0.03 seconds
2 tests, 0 failures

Another attribute we can add to our functions is @spec, which then allows us to define the types that the inputs and outputs of the function will take. It then gets added to pages i.e. documentation created by ExDoc and allows us to get a better understanding of how to use the function. For example:

@spec multiply(integer, integer) :: integer
def multiply(num1, num2) do
  num1 * num2
end

This will show up in our documentation as follows:
enter image description here

Further reading and references on DocTesting:
DocTests in Elixir

Thanks to josevalim and milmazz, owners and maintainers of ExDoc.