Write Some Basic Tests

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.

project/0/issues/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.

project/1/issues/test/cli_test.exs
 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.

project/1/issues/lib/issues/cli.ex
 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