Lucid Simple

ExUnit Cheat Sheet

There is a lot of good documentation on ExUnit, not the least of which is the ExUnit section at elixir-lang.org.

This little cheat sheet isn’t meant to be comprehensive, but instead is a list of the ExUnit techniques I use on almost every project.

Table of Contents

  1. Skip Pending Tests
  2. Run Only Certain Tests
  3. Silence STDOUT
  4. Ensure a Service is Running
  5. Load Support Modules
  6. Workhorse Assertions
  7. Setup and Other Callbacks
  8. Start and Stop the Application
  9. VCR and Mocking
  10. Use the Test Seed to Fight Intermittent Errors
  11. Check that your Process is Alive

1. Skip Pending Tests

If you want to mark certain tests as pending and automatically exclude them from your test runs, use ExUnit.configure in your test helper:

# test_helper.exs
ExUnit.configure(exclude: [pending: true])

ExUnit.start()

Then you can use a tag to mark it as pending which will cause it to be skipped when you run mix test:

defmodule SomeModuleTest do
  use ExUnit.Case

  @tag :pending
  test "my pending test" do
    assert 2 + 2 == 5
  end
end

Pending Test(/assets/pending.png)

From the output above you can see that the pending test was skipped.

If you actually want to run your pending tests later on, you can do this:

mix test --include pending

back to the top

2. Run Only Certain Tests

If you had some WIP tests, for example, you could select only those to run with the only flag:

mix test --only wip
defmodule SomeModuleTest do
  use ExUnit.Case

  test "some test" do
    assert 1 + 1 == 2
  end

  @tag :pending
  test "my pending test" do
    assert 2 + 2 == 5
  end

  @tag :wip
  test "my work in progress test" do
    assert 2 + 2 == 4
  end
end

Running WIP Tests(/assets/wip.png)

If you read the output above, you’ll see that only 1 of the three tests ran.

In this case, only tests marked wip were run.

Similarly, you could exclude your WIP tests this way:

mix test --exclude wip

back to the top

3. Silence STDOUT

I don’t like it when my test output is littered with print or log messages.

If you have Elixir version 1.1+ then you can use ExUnit.CaptureIO to keep things clean (among other uses):

defmodule SomeModuleTest do
  use ExUnit.Case

  import ExUnit.CaptureIO

  test "some noisy test" do
    capture_io fn ->
      IO.puts "RAWR"
    end

    assert 1 + 1 == 2
  end
end

If you wrap the noise producing parts in a function and pass it to capture_io, then the output will be suppressed.

In the above example “RAWR” will not end up with the test results.

Don’t fret If your version of Elixir doesn’t have CaptureIO. Other techniques may still work.

back to the top

4. Ensure a Service is Running

This isn’t ExUnit specific, per se, but if your test suite depends on a particular service running (e.g Redis), then you can test for it when your test helper gets loaded.

That way, if the service isn’t available then you have the opportunity to abort the test run and print a friendly error message:

# test_helper.exs
ExUnit.start()

case :gen_tcp.connect('localhost', 6379, []) do
  {:ok, socket} ->
    :gen_tcp.close(socket)
  {:error, reason} ->
    Mix.raise "Cannot connect to Redis" <>
              " (http://localhost:6379):" <>
              " #{:inet.format_error(reason)}"
end

This example comes from Andrea Leopardi’s great Redix project.

He uses :get_tcp to check that he can connect to port 6379. If the connection is successful, he closes the socket and the test run continues. If the connection is not successful, he uses Mix.raise with a helpful error message to shut things down.

Pretty cool.

back to the top

5. Load Support Modules

It’s not a problem to create support modules directly in your test files, but if they get too big then you may want to move them to their own directory.

This technique comes from Mike Pack’s Poolboy Queue library:

# test_helper.exs
ExUnit.start()

{:ok, files} = File.ls("./test/support")

Enum.each files, fn(file) ->
  Code.require_file "support/#{file}", __DIR__
end

He uses File.ls to fetch the names of all of the files living under test/support and then applies Code.require_file to each one.

Once again, this isn’t ExUnit specific, but this simple technique pops up fairly frequently in my own projects.

back to the top

6. Workhorse Assertions

The best source on ExUnit assertions is still the docs at elixir-lang.org.

However, I use the following 3 assertions roughly 80% of the time:

They are mercifully self-explanatory:

# assert
test "a simple assertion" do
  assert 2 + 2 == 4
end

# refute
test "a simple refutation" do
  refute 2 + 2 == 5
end

# assert_receive
test "some service calls me back" do
  SomeService.work(self())

  assert_receive {:result, "some-result"}, 100
end

The final example might warrant an explanation: I’m asserting that my test process will eventually receive the term {:result, "some-result"} within 100 milliseconds. If the expected message doesn’t arrive on time, the assertion will fail.

assert_receive and its cousin assert_received are pretty crucial given that message passing is at the heart of Elixir and Erlang.

back to the top

7. Setup and Other Callbacks

I really can’t do any better than the great explanation at the elixir-lang.org site, but in case it’s not obvious:

You’ll use on_exit in the same way you might use teardown in a different language or test framework.

back to the top

8. Start or Stop the Application

By default, your application will be started while your tests run.

If you don’t want that behavior, then you could stop it in the test helper:

ExUnit.start()

Application.stop(:your_app_name)

This is sometimes beneficial if you find that your running application is competing with your unit tests.

Thankfully, you shouldn’t have to do this too much if you’re properly using your application config files. But it’s still nice to have the option.

In case you don’t remember, the name of your app is listed in your mixfile:

# mix.exs
def project do
    [app: :your_app_name,
     # ...
    ]
end

To get things started again you only need to call start:

Application.start(:your_app_name)

back to the top

9. VCR and Mocking

If you’re coming from the Ruby world and would like something similar to VCR to fake HTTP responses, then check out ExVCR.

Similarly, you can use Meck if you want a nice mocking library.

I have used both and they work very well, but you’ll need to take care that you’re not straying too far from the Elixir way if you use them.

back to the top

10. Use the Test Seed to Fight Intermittent Errors

Intermittent errors are probably unavoidable if you’re writing concurrent programs.

To help fight this, ExUnit tests are run in a random order via a seed integer.

If a particular run results in errors that you don’t normally see, you can run subsequent tests in the exact same order using that seed as a flag:

mix test --seed <the-seed-that-triggered-errors>

The seed will be printed at the bottom of the test output in the form of

Randomized with seed <THE-SEED-VALUE>

Using the Seed(/assets/seed.png)

If you look at the output above, you’ll see that we’re passing the integer 668506 to mix.

No matter how many times we run mix test --seed 668506 we’ll always get the tests run in the exact same order.

Getting consistent results goes a long way in squashing pesky errors that only occur some of the time.

back to the top

11. Check that your Process is Alive

If you have a reference to a process ID, you can use

Process.alive?(pid)

If you have a name instead of a pid, you can use whereis to lookup the pid before passing it to alive?:

process_name |> Process.whereis |> Process.alive?

It would probably be worth your time to check out all the cool things that Process can do when you get a chance.

back to the top

Closing

I wrote this post for myself so that I can easily lookup the techniques I use frequently but still manage to forget.

I hope you will find at least some use from it.

Here are some other articles you might be interested in: