Introducing assertions
, the library that helps you write really great test assertions!
GitHub: https://github.com/devonestes/assertions
Hex.pm: https://hex.pm/packages/assertions
HexDocs: https://hexdocs.pm/assertions/Assertions.html
This library aims to wrap up common types of assertions that many applications come across in a nice, reusable, composable fashion, and with really exceptional error messages.
Here’s just one example of one use case - comparing a list of structs for equality.
Imagine you have a Phoenix app, and you need to test that the response from a function is basically equal to some other list of structs that you created earlier. We can’t compare them directly because the order of the structs in the list is not guaranteed, so assert list1 == list2
won’t work. Plus, the structs in our list might have differing assocations preloaded or not, meaning we also can’t reliably do assert user1 == hd(list)
. So, to test this reliably, we’ll need to do something like this:
defmodule UsersTest do
use ExUnit.Case, async: true
describe "update_all/2" do
test "updates the given users in the database and returns those updated users" do
alice = Factory.insert(:user, name: "Alice")
bob = Factory.insert(:user, name: "Bob")
updated_names =
[{alice, %{name: "Alice A."}, {bob, %{name: "Bob B."}}}]
|> Users.update_all()
|> Enum.map(& &1.name)
all_user_names =
User
|> Repo.all()
|> Enum.map(& &1.name)
Enum.each(["Alice A.", "Bob B."], fn name ->
assert name in updated_names
assert name in all_user_names
end
end
end
end
But that leaves a lot to be desired. There’s extra code to pull out just the names from the response and we’re obscuring the function that we’re actually testing here. The call to Users.update_all/1
is kind of buried in there.
Let’s see how we might do this with assertions
:
defmodule UsersTest do
use ExUnit.Case, async: true
describe "update_all/2" do
test "updates the given users in the database and returns those updated users" do
alice = Factory.insert(:user, name: "Alice")
bob = Factory.insert(:user, name: "Bob")
result = Users.update_all([{alice, %{name: "Alice A."}, {bob, %{name: "Bob B."}}}
result
|> Enum.map(& &1.name)
|> assert_lists_equal(["Alice A.", "Bob B."])
assert_lists_equal(result, Users.list_all(), &structs_equal?(&1, &2, [:name]))
end
end
end
It’s shorter, easier to change, and much more clear about the function being tested in this test. It also gives us really wonderful error messages, which you can see more examples of in the README on GitHub.
There are many more assertions there, and if there are any other common ones that folks uses often in their projects I’d be happy to add them. I want this to be the one library that has all the common helper functions that anyone needs to write really great tests!