Documentation and Tests in Elixir

Keeping Docs and Tests in Sync

billperegoy
im-becoming-functional
4 min readAug 8, 2017

--

Below the Surface of Elixir…

I’ve been pretty excited about some of the obviously cool things about Elixir:

  • It’s a cool functional language with largely immutable state
  • It has a super user-friendly, Ruby-like syntax
  • It’s the language under the hood of the Phoenix web framework
  • It includes OTP functionality that provides out of the box multiprocessing capabilities along with fault tolerance

It’s easy to get lost in all of that capability and miss out on some of the less sexy, but maybe more important capabilities. In this blog post, I’ll run through my experience generating documentation and using that documentation to automatically generate tests.

Generating Documentation from Source Code

The biggest issue with documentation is that it is usually out of sync with the actual code. We will often write documentation that matches the initial code. Then requirements change, we change the code to match these requirements, and neglect to modify the documentation. We can make this possibility slightly less likely by including our documentation directly in our tests.

You’re probably used to seeing Elixir modules documented on interactive HTML pages that look like this.

This format shows all the public functions in a module along with documentation and examples for each function.

This can be automatically generated from module attributes inside your Elixir code. Module attributes are constants that can be interpreted by the Elixir compiler or other tools that process source code. We use the @moduledoc attribute to provide documentation for the module and the @doc attribute to provide documentation for each function. It’s probably easiest to look at an example module.

You’ll note that I’ve included a @moduledoc attribute at the top of the module and a @doc attribute before each public function. The documentation is included within a triple quoted string sigil. Within this string I’ve included some markdown that describes the function along with some examples formatted just like they would be within the Elixir REPL.

With this documentation in place, we can then use the ExDoc package to convert this to a nicely formatted HTML document. You can follow the instructions to install the build the ExDoc package in the README file.

With that installation done, you can then compile your project and run ex_doc as shown below (just change the ex_doc path to match your installation location).

% cd <your-project-path>
% mix compile
% /Users/bperegoy/Projects/ex_doc/bin/ex_doc \
“sentence” \
“1.0.0” \
_build/dev/lib/sentence/ebin

Once you’ve done this, if you look in the doc directory of your project, you’ll see a newly generated index.html and associated files. You can load index.html into a browser and you should see nicely formatted documentation that is 100% derived from the attributes in your code.

Turing Documentation into Tests

Documentation is great when it is easily accessible and accurate. The ExDoc package solves one half of that problem. Putting documentation in the code makes it easy to create and deploy. But keeping documentation accurate is much more difficult. It’s way too easy to change code and forget to update the documentation. Using ExDoc by itself does nothing to solve this problem. There’s no reason that running ExDoc will prove that the examples you’ve provided actually work with the current version of the code.

Fortunately Elixir’s unit test tool solves this problem for us. ExUnit has the ability to automatically read the ExDoc examples and turn these into tests. It’s as simple as writing tests that look like this.

defmodule SentenceTest do
use ExUnit.Case
doctest Sentence
end

If you add the doctest line shown above to your test file, ExUnit will convert of the examples from sentence.ex and run them as unit tests. It will then report failures using the line numbers in your source code examples. It’s really that simple!

If your unit tests pass, this means your code completely matches the examples provided in the documentation. Thus any documentation published with ExDoc, is guaranteed to be as correct as your tests.

Conclusion

The Elixir mix environment provides everything you need to include unit tests with your source code, run these unit tests in an automated fashion, and convert this documentation into easy to publish HTML documentation of your functions.

I want an environment that makes it easy for me to do the tasks most important to creating quality production code. Testing and documentation are two of most important task here. The combination of Elixir, mix, ExUnit and ExDoc make these tasks easy to integrate and result in better quality code. This is one more reason I’m looking to move more towards Elixir for future backend projects.

--

--

billperegoy
im-becoming-functional

Polyglot programmer exploring the possibilities of functional programming.