At this point, I get a little nervous if I don’t have some tests. Fortunately, Elixir comes with a wonderful (and simple) testing framework called ExUnit.
Have a look at the file test/issues_test.exs.
| defmodule IssuesTest do |
| use ExUnit.Case |
| doctest Issues |
| |
| test "greets the world" do |
| assert Issues.hello() == :world |
| end |
| end |
It acts as a template for all the test files you write. I just copy and paste the boilerplate into separate test files as I need them. So let’s write tests for our CLI module, putting those tests into the file test/cli_test.exs. (Test file names must end with _test.) We’ll test that the option parser successfully detects the -h and --help options, and that it returns the arguments otherwise. We’ll also check that it supplies a default value for the count if only two arguments are given.
| defmodule CliTest do |
| use ExUnit.Case |
| doctest Issues |
| |
| import Issues.CLI, only: [ parse_args: 1 ] |
| |
| test ":help returned by option parsing with -h and --help options" do |
| assert parse_args(["-h", "anything"]) == :help |
| assert parse_args(["--help", "anything"]) == :help |
| end |
| |
| test "three values returned if three given" do |
| assert parse_args(["user", "project", "99"]) == { "user", "project", 99 } |
| end |
| |
| test "count is defaulted if two values given" do |
| assert parse_args(["user", "project"]) == { "user", "project", 4 } |
| end |
| end |
These tests all use the basic assert macro that ExUnit provides. This macro is clever—if an assertion fails, it can extract the values from the expression you pass it, giving you a nice error message.
To run our tests, we’ll use the mix test task.
| issues$ mix test |
| Compiled lib/issues.ex |
| Compiled lib/issues/cli.ex |
| Generated issues app |
| .. |
| |
| Failures: |
| |
| 1) test three values returned if three given (CliTest) |
| test/cli_test.exs:11 |
| Assertion with == failed |
| code: parse_args(["user", "project", "99"]) == {"user", "project", 99} |
| lhs: {"user", "project", "99"} |
| rhs: {"user", "project", 99} |
| stacktrace: |
| test/cli_test.exs:13 |
| . |
| Finished in 0.01 seconds |
| 4 tests, 1 failures |
One of the four tests failed. When we pass a count as the third parameter, our code blows up. See how the assertion shows you its type (== in this case), the line of code that failed, and the two values that we compared. You can see the difference between the left-hand side (lhs), which is the value returned by parse_args, and the expected value (the rhs)—if your terminal and your eyes support it, you’ll see that the "99" in the line labelled lhs: is colored red, and the 99 in the next line is green. We were expecting to get a number as the count, but we got a string.
That’s easily fixed. The built-in function String.to_integer converts a binary (a string) into an integer.
| def parse_args(argv) do |
| parse = OptionParser.parse(argv, switches: [ help: :boolean], |
| aliases: [ h: :help ]) |
| case parse do |
| |
| { [ help: true ], _, _ } -> :help |
» | { _, [ user, project, count ], _ } -> { user, project, |
» | String.to_integer(count) } |
| { _, [ user, project ], _ } -> { user, project, @default_count } |
| _ -> :help |
| end |
| end |