ExUnit Testing Framework
ExUnit is a testing framework built specifically for the Elixir programming language.
It provides a simple and powerful way to write tests for your Elixir projects, ensuring that your code is reliable and bug-free. In this tutorial, we will explore the history, features, and examples of ExUnit.
History of ExUnit
ExUnit was first introduced in Elixir version 0.9.0 and has since become the standard testing framework for Elixir applications. It was heavily influenced by other testing frameworks, such as Ruby's RSpec and Erlang's eunit, but was designed to take full advantage of Elixir's features and syntax.
Features of ExUnit
ExUnit offers a wide range of features that make it an excellent choice for testing Elixir code. Some of the key features include:
Test Organization
ExUnit allows you to organize your tests into test modules, which are defined as modules that use the ExUnit.Case module. You can group related tests together and define setup and teardown functions for each module.
Here's an example of a basic test module:
defmodule MathTest do
use ExUnit.Case
test "addition" do
assert 2 + 2 == 4
end
end
In this example, we define a test module called MathTest that uses the ExUnit.Case module. Inside the module, we define a test using the test macro. The test checks if the addition of 2 and 2 is equal to 4 using the assert macro.
Assertions
ExUnit provides a variety of built-in assertion macros that allow you to make assertions about the behavior of your code. Some of the most commonly used assertions include assert, refute, assert_equal, assert_match, and assert_receive.
Here's an example that demonstrates some of these assertions:
defmodule MathTest do
use ExUnit.Case
test "addition" do
assert 2 + 2 == 4
end
test "subtraction" do
refute 5 - 3 == 2
end
test "multiplication" do
assert_equal 5 * 2, 10
end
test "division" do
assert_match ~r/Invalid division/, fn ->
5 / 0
end
end
test "message" do
assert_receive {:message, "Hello"}
end
end
In this example, we have added more tests to our MathTest module. We use different assertions to check the expected behavior of addition, subtraction, multiplication, division, and message passing.
Setup and Teardown
ExUnit allows you to define setup and teardown functions at different levels of granularity. You can define setup and teardown functions for individual tests, test modules, or the entire test suite.
Here's an example that demonstrates the use of setup and teardown functions:
defmodule MathTest do
use ExUnit.Case
setup do
{:ok, state: 42}
end
test "addition", %{state: state} do
assert 2 + 2 + state == 46
end
test "subtraction", %{state: state} do
refute 5 - 3 - state == 40
end
teardown do
:ok
end
end
In this example, we define a setup function that sets up a state with the value of 42. The state is then passed as an argument to each test. We also define a teardown function that runs after each test.
Tags and Filters
ExUnit allows you to tag your tests and filter them based on these tags. Tags can be used to group tests, mark tests as pending, or exclude certain tests from running.
Here's an example that demonstrates the use of tags and filters:
defmodule MathTest do
use ExUnit.Case
@tag :slow
test "addition" do
assert 2 + 2 == 4
end
@tag :pending
test "subtraction" do
refute 5 - 3 == 2
end
end
In this example, we have added tags to our tests using the @tag attribute. We can then use the --include and --exclude options when running tests to filter them based on their tags.
Examples of ExUnit
Now, let's take a look at a few more examples of how ExUnit can be used.
Testing a Function
defmodule StringUtilsTest do
use ExUnit.Case
test "string length" do
assert String.length("Hello") == 5
end
end
In this example, we define a test module called StringUtilsTest and a test that checks the length of a string using the String.length function.
Testing a Process
defmodule CounterTest do
use ExUnit.Case
test "increment" do
pid = spawn(fn -> Counter.increment() end)
assert_receive {:ok, _}
end
end
In this example, we define a test module called CounterTest and a test that checks if a process can increment a counter using the Counter.increment function. We use the spawn function to create a new process and the assert_receive macro to check for a specific message.
Testing Exception Handling
defmodule DivisionTest do
use ExUnit.Case
test "division by zero" do
assert_raise ZeroDivisionError, fn ->
5 / 0
end
end
end
In this example, we define a test module called DivisionTest and a test that checks if dividing by zero raises a ZeroDivisionError exception. We use the assert_raise macro to assert that an exception is raised within the given function.
Conclusion
In this tutorial, we explored the introduction, history, features, and examples of ExUnit, Elixir's testing framework. ExUnit provides a powerful and flexible way to write tests for your Elixir applications. By using ExUnit, you can ensure that your code is reliable, bug-free, and well-tested.
For more information, you can refer to the official ExUnit documentation: ExUnit Documentation