Testing
Learn different types of testing in Elixir.
We'll cover the following
We already used the ExUnit
framework to write tests for our Issues
tracker application. But that chapter only scratched the surface of Elixir testing. Let’s dig deeper.
Testing the comments
In the Elixir world, a common way to document functions is by showing the function being used in an IEx session.
Let’s look at an example from our Issues
application. The TableFormatter
formatter module defines a number of self-contained functions that we can document.
defmodule Issues.TableFormatter do
import Enum, only: [ each: 2, map: 2, map_join: 3, max: 1 ]
@doc """
The code takes a list of row data, where each row is a map, and a list of headers. It prints a table to standard output of the data from each row identified by each header. That is, each header identifies a column, and those columns are extracted and printed from the rows. We calculate the width of each column to fit the longest element in that column.
"""
def print_table_for_columns(rows, headers) do
with data_by_columns = split_into_columns(rows, headers),
column_widths = widths_of(data_by_columns),
format = format_for(column_widths)
do
puts_one_line_in_columns(headers, format)
IO.puts(separator(column_widths))
puts_in_columns(data_by_columns, format)
end end
@doc """
Given a list of rows where each row contains a keyed list of columns, the code returns a list containing lists of the data in each column. The headers
parameter contains the list of columns to extract.
Run the commands above to execute the code below.
## Example
iex> list = [Enum.into([{"a", "1"},{"b", "2"},{"c", "3"}], %{}),
...> Enum.into([{"a", "4"},{"b", "5"},{"c", "6"}], %{})]
iex> Issues.TableFormatter.split_into_columns(list, [ "a", "b", "c" ])
[ ["1", "4"], ["2", "5"], ["3", "6"] ]
"""
def split_into_columns(rows, headers) do
for header <- headers do
for row <- rows, do: printable(row[header])
end
end
@doc """
the code returns a binary (string) version of our parameter.
## Examples
iex> Issues.TableFormatter.printable("a")
"a"
iex> Issues.TableFormatter.printable(99)
"99"
"""
def printable(str) when is_binary(str), do: str
def printable(str), do: to_string(str)
@doc """
Given a list containing sublists, where each sublist contains the data for a column, the code returns a list containing the maximum width of each column.
## Example
iex> data = [ [ "cat", "wombat", "elk"], ["mongoose", "ant", "gnu"]]
iex> Issues.TableFormatter.widths_of(data)
[ 6, 8 ]
"""
def widths_of(columns) do
for column <- columns, do: column |> map(&String.length/1) |> max
end
@doc """
Given a list containing rows of data, a list containing the header selectors, and a format string, this writes the extracted data under control of the format string.
"""
def puts_in_columns(data_by_columns, format) do
data_by_columns
|> List.zip
|> map(&Tuple.to_list/1)
|> each(&puts_one_line_in_columns(&1, format))
end
def puts_one_line_in_columns(fields, format) do
:io.format(format, fields)
end
end
Note how some of the documentation contains sample IEx sessions. This helps people who come along later understand how to use the code. Just as important , it lets us understand what our code will feel like to use.
But the problem with comments is that they just don’t get maintained. The code changes, the comment gets stale, and it becomes useless. Fortunately, ExUnit
has doctest
, which is a tool that extracts the iex
sessions from the code’s @doc
strings, runs it, and checks that the output agrees with the comment.
To invoke it, we simply add one or more
doctest «ModuleName»
lines to the test files. We can add them to existing test files for a module (such as table_formatter_test.exs
) or create a new test file just for them. the latter is what we’ll do here. Let’s create a new test file called test/doc_test.exs
in the code below.
We exit from iex
session using “Ctrl+C” twice and then we can run the following commands:
$ mix test test/doc_test.exs
......
Finished in 0.00 seconds
5 doctests, 0 failures
And, of course, these tests are integrated into the overall test suite:
$ mix test
..............
Finished in 0.1 seconds
5 doctests, 9 tests, 0 failures
Get hands-on with 1400+ tech skills courses.