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
assert/1functions that are provided by
ExUnitwhen we run the tests
- We would like to expose the
error_messagevalue 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.