Custom ExUnit Assertion
Writing an assert_invalid
macro for ExUnit assertions. Implementation at the bottom.
I was working a lot with Ecto Changesets - using them for validation in my HTTP application. The application code (the code in my lib/
folder) was very nice, but I was not happy with the verbosity of the tests. Each test looked something like this:
This was okay for a single test about a single field, but quickly lead to a tonne of copy-and-paste across multiple tests that were asserting on multiple fields.
The solution was to create a custom assertion that could combine and abstract the multiple assertions away.
Here is an example test suite which uses assert_invalid
(my creation) and shows off the error messages when the assertions fail — I think having good ExUnit
error messages is the most important part!
Here is the resulting output:
Most importantly, here is the code:
It is necessary for the assertion to be a macro for two reasons:
- We need access to the
flunk/1
andassert/1
functions that are provided byExUnit
when we run the tests - We would like to expose the
error_message
value so that users can write expressions against it
The most interesting part of the code is the call to Kernel.var!/2
on line 15. By default, variables declared inside of a macro are hygienic and will not impact variables defined where the macro is expanded. Using Kernel.var!/2
is one way to explicitly disable that hygiene and have the error_message
value be available in our tests.
Additionally, using the IO.ANSI
module to color my stdout
really helped make the assertion errors more readable:
I am not a metaprogramming expert, but hopefully this example code will be useful for other folks getting started with writing their own assertions.